killbill-aplcache

subscription: deprecate SubscriptionBaseInternalApi#createSubscription Consolidate

4/18/2018 11:47:53 AM

Changes

Details

diff --git a/api/src/main/java/org/killbill/billing/subscription/api/SubscriptionBaseInternalApi.java b/api/src/main/java/org/killbill/billing/subscription/api/SubscriptionBaseInternalApi.java
index ac9f05f..2a05cf6 100644
--- a/api/src/main/java/org/killbill/billing/subscription/api/SubscriptionBaseInternalApi.java
+++ b/api/src/main/java/org/killbill/billing/subscription/api/SubscriptionBaseInternalApi.java
@@ -33,7 +33,6 @@ import org.killbill.billing.catalog.api.Catalog;
 import org.killbill.billing.catalog.api.CatalogApiException;
 import org.killbill.billing.catalog.api.PlanPhasePriceOverride;
 import org.killbill.billing.catalog.api.PlanPhaseSpecifier;
-import org.killbill.billing.entitlement.api.BaseEntitlementWithAddOnsSpecifier;
 import org.killbill.billing.entitlement.api.EntitlementAOStatusDryRun;
 import org.killbill.billing.events.EffectiveSubscriptionInternalEvent;
 import org.killbill.billing.invoice.api.DryRunArguments;
@@ -43,20 +42,14 @@ import org.killbill.billing.util.entity.Pagination;
 
 public interface SubscriptionBaseInternalApi {
 
-    public SubscriptionBase createSubscription(SubscriptionBaseBundle bundle,
-                                               @Nullable SubscriptionBase baseSubscription,
-                                               PlanPhaseSpecifier spec,
-                                               List<PlanPhasePriceOverride> overrides,
-                                               DateTime requestedDateWithMs,
-                                               boolean isMigrated,
-                                               InternalCallContext context) throws SubscriptionBaseApiException;
-
-    public List<SubscriptionBaseWithAddOns> createBaseSubscriptionsWithAddOns(UUID accountId, Iterable<BaseEntitlementWithAddOnsSpecifier> baseEntitlementWithAddOnsSpecifier,
-                                                                              boolean renameCancelledBundleIfExist, InternalCallContext contextWithValidAccountRecordId) throws SubscriptionBaseApiException;
+    public List<SubscriptionBaseWithAddOns> createBaseSubscriptionsWithAddOns(Iterable<SubscriptionBaseWithAddOnsSpecifier> subscriptionWithAddOnsSpecifiers,
+                                                                              boolean renameCancelledBundleIfExist,
+                                                                              InternalCallContext contextWithValidAccountRecordId) throws SubscriptionBaseApiException;
 
     public void cancelBaseSubscriptions(Iterable<SubscriptionBase> subscriptions, BillingActionPolicy policy, int accountBillCycleDayLocal, InternalCallContext context) throws SubscriptionBaseApiException;
 
-    public SubscriptionBaseBundle createBundleForAccount(UUID accountId, String bundleName, boolean renameCancelledBundleIfExist, InternalCallContext context)
+    //@VisibleForTesting
+    SubscriptionBaseBundle createBundleForAccount(UUID accountId, String bundleName, boolean renameCancelledBundleIfExist, InternalCallContext context)
             throws SubscriptionBaseApiException;
 
     public List<SubscriptionBaseBundle> getBundlesForAccountAndKey(UUID accountId, String bundleKey, InternalTenantContext context)
diff --git a/api/src/main/java/org/killbill/billing/subscription/api/SubscriptionBaseWithAddOnsSpecifier.java b/api/src/main/java/org/killbill/billing/subscription/api/SubscriptionBaseWithAddOnsSpecifier.java
new file mode 100644
index 0000000..e6a7099
--- /dev/null
+++ b/api/src/main/java/org/killbill/billing/subscription/api/SubscriptionBaseWithAddOnsSpecifier.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright 2014-2018 Groupon, Inc
+ * Copyright 2014-2018 The Billing Project, LLC
+ *
+ * The Billing Project licenses this file to you under the Apache License, version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at:
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.killbill.billing.subscription.api;
+
+import java.util.UUID;
+
+import org.joda.time.DateTime;
+import org.killbill.billing.entitlement.api.EntitlementSpecifier;
+
+public class SubscriptionBaseWithAddOnsSpecifier {
+
+    private final UUID bundleId;
+    private final String bundleExternalKey;
+    private final Iterable<EntitlementSpecifier> entitlementSpecifiers;
+    private final DateTime billingEffectiveDate;
+    private final boolean isMigrated;
+
+    public SubscriptionBaseWithAddOnsSpecifier(final UUID bundleId,
+                                               final String bundleExternalKey,
+                                               final Iterable<EntitlementSpecifier> entitlementSpecifiers,
+                                               final DateTime billingEffectiveDate,
+                                               final boolean isMigrated) {
+        this.bundleId = bundleId;
+        this.bundleExternalKey = bundleExternalKey;
+        this.entitlementSpecifiers = entitlementSpecifiers;
+        this.billingEffectiveDate = billingEffectiveDate;
+        this.isMigrated = isMigrated;
+    }
+
+    public UUID getBundleId() {
+        return bundleId;
+    }
+
+    public String getBundleExternalKey() {
+        return bundleExternalKey;
+    }
+
+    public Iterable<EntitlementSpecifier> getEntitlementSpecifiers() {
+        return entitlementSpecifiers;
+    }
+
+    public DateTime getBillingEffectiveDate() {
+        return billingEffectiveDate;
+    }
+
+    public boolean isMigrated() {
+        return isMigrated;
+    }
+
+    @Override
+    public String toString() {
+        final StringBuilder sb = new StringBuilder("SubscriptionBaseWithAddOnsSpecifier{");
+        sb.append("bundleId=").append(bundleId);
+        sb.append(", bundleExternalKey='").append(bundleExternalKey).append('\'');
+        sb.append(", entitlementSpecifiers=").append(entitlementSpecifiers);
+        sb.append(", billingEffectiveDate=").append(billingEffectiveDate);
+        sb.append(", isMigrated=").append(isMigrated);
+        sb.append('}');
+        return sb.toString();
+    }
+}
diff --git a/entitlement/src/main/java/org/killbill/billing/entitlement/api/DefaultEntitlementApi.java b/entitlement/src/main/java/org/killbill/billing/entitlement/api/DefaultEntitlementApi.java
index 42356de..48204b3 100644
--- a/entitlement/src/main/java/org/killbill/billing/entitlement/api/DefaultEntitlementApi.java
+++ b/entitlement/src/main/java/org/killbill/billing/entitlement/api/DefaultEntitlementApi.java
@@ -19,8 +19,10 @@
 package org.killbill.billing.entitlement.api;
 
 import java.util.ArrayList;
+import java.util.Collection;
 import java.util.HashMap;
 import java.util.Iterator;
+import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
 import java.util.UUID;
@@ -57,6 +59,7 @@ import org.killbill.billing.security.api.SecurityApi;
 import org.killbill.billing.subscription.api.SubscriptionBase;
 import org.killbill.billing.subscription.api.SubscriptionBaseInternalApi;
 import org.killbill.billing.subscription.api.SubscriptionBaseWithAddOns;
+import org.killbill.billing.subscription.api.SubscriptionBaseWithAddOnsSpecifier;
 import org.killbill.billing.subscription.api.transfer.SubscriptionBaseTransferApi;
 import org.killbill.billing.subscription.api.transfer.SubscriptionBaseTransferApiException;
 import org.killbill.billing.subscription.api.user.SubscriptionBaseApiException;
@@ -182,6 +185,7 @@ public class DefaultEntitlementApi extends DefaultEntitlementApiBase implements 
                 final DateTime now = clock.getUTCNow();
 
                 final Iterable<BaseEntitlementWithAddOnsSpecifier> baseEntitlementWithAddOnsSpecifiersAfterPlugins = updatedPluginContext.getBaseEntitlementWithAddOnsSpecifiers();
+                final Collection<SubscriptionBaseWithAddOnsSpecifier> subscriptionBaseWithAddOnsSpecifiers = new LinkedList<SubscriptionBaseWithAddOnsSpecifier>();
                 DateTime upTo = null;
                 for (final BaseEntitlementWithAddOnsSpecifier baseEntitlementWithAddOnsSpecifier : baseEntitlementWithAddOnsSpecifiersAfterPlugins) {
                     // Entitlement
@@ -189,6 +193,15 @@ public class DefaultEntitlementApi extends DefaultEntitlementApiBase implements 
                                                                                                        now,
                                                                                                        contextWithValidAccountRecordId);
                     upTo = upTo == null || upTo.compareTo(entitlementRequestedDate) < 0 ? entitlementRequestedDate : upTo;
+
+                    final DateTime billingEffectiveDateTime = (baseEntitlementWithAddOnsSpecifier.getBillingEffectiveDate() != null) ?
+                                                              contextWithValidAccountRecordId.toUTCDateTime(baseEntitlementWithAddOnsSpecifier.getBillingEffectiveDate()) : null;
+                    final SubscriptionBaseWithAddOnsSpecifier subscriptionBaseWithAddOnsSpecifier = new SubscriptionBaseWithAddOnsSpecifier(baseEntitlementWithAddOnsSpecifier.getBundleId(),
+                                                                                                                                            baseEntitlementWithAddOnsSpecifier.getExternalKey(),
+                                                                                                                                            baseEntitlementWithAddOnsSpecifier.getEntitlementSpecifier(),
+                                                                                                                                            billingEffectiveDateTime,
+                                                                                                                                            baseEntitlementWithAddOnsSpecifier.isMigrated());
+                    subscriptionBaseWithAddOnsSpecifiers.add(subscriptionBaseWithAddOnsSpecifier);
                 }
 
                 try {
@@ -196,8 +209,7 @@ public class DefaultEntitlementApi extends DefaultEntitlementApiBase implements 
                     // Note that to fully check for block_change we should also look for BlockingState at the BUNDLE/SUBSCRIPTION level in case some of the input contain a BP that already exists.
                     checkForAccountBlockingChange(accountId, upTo, contextWithValidAccountRecordId);
 
-                    final List<SubscriptionBaseWithAddOns> subscriptionsWithAddOns = subscriptionBaseInternalApi.createBaseSubscriptionsWithAddOns(accountId,
-                                                                                                                                                   baseEntitlementWithAddOnsSpecifiersAfterPlugins,
+                    final List<SubscriptionBaseWithAddOns> subscriptionsWithAddOns = subscriptionBaseInternalApi.createBaseSubscriptionsWithAddOns(subscriptionBaseWithAddOnsSpecifiers,
                                                                                                                                                    renameCancelledBundleIfExist,
                                                                                                                                                    contextWithValidAccountRecordId);
                     final Map<BlockingState, UUID> blockingStateMap = new HashMap<BlockingState, UUID>();
@@ -312,25 +324,20 @@ public class DefaultEntitlementApi extends DefaultEntitlementApiBase implements 
                 }
 
                 try {
-                    final BaseEntitlementWithAddOnsSpecifier baseEntitlementWithAddOnsSpecifier = getFirstBaseEntitlementWithAddOnsSpecifier(updatedPluginContext.getBaseEntitlementWithAddOnsSpecifiers());
-                    final EntitlementSpecifier specifier = getFirstEntitlementSpecifier(baseEntitlementWithAddOnsSpecifier);
-
-                    final DateTime billingRequestedDateRaw = dateHelper.fromLocalDateAndReferenceTime(baseEntitlementWithAddOnsSpecifier.getBillingEffectiveDate(), now, context);
-
-                    DateTime billingRequestedDate = billingRequestedDateRaw;
-                    if (!isStandalone) {
-                        final DateTime baseSubscriptionStartDate = eventsStreamForBaseSubscription.getSubscriptionBase().getStartDate();
-                        billingRequestedDate = billingRequestedDateRaw.isBefore(baseSubscriptionStartDate) ? baseSubscriptionStartDate : billingRequestedDateRaw;
-                    }
+                    final BaseEntitlementWithAddOnsSpecifier baseEntitlementWithAddOnsSpecifierAfterPlugins = getFirstBaseEntitlementWithAddOnsSpecifier(updatedPluginContext.getBaseEntitlementWithAddOnsSpecifiers());
+                    final DateTime billingEffectiveDateTime = (baseEntitlementWithAddOnsSpecifierAfterPlugins.getBillingEffectiveDate() != null) ?
+                                                          context.toUTCDateTime(baseEntitlementWithAddOnsSpecifierAfterPlugins.getBillingEffectiveDate()) : null;
+                    final SubscriptionBaseWithAddOnsSpecifier subscriptionBaseWithAddOnsSpecifier = new SubscriptionBaseWithAddOnsSpecifier(baseEntitlementWithAddOnsSpecifierAfterPlugins.getBundleId(),
+                                                                                                                                            baseEntitlementWithAddOnsSpecifierAfterPlugins.getExternalKey(),
+                                                                                                                                            baseEntitlementWithAddOnsSpecifierAfterPlugins.getEntitlementSpecifier(),
+                                                                                                                                            billingEffectiveDateTime,
+                                                                                                                                            baseEntitlementWithAddOnsSpecifierAfterPlugins.isMigrated());
 
                     final SubscriptionBaseBundle baseBundle = subscriptionBaseInternalApi.getBundleFromId(bundleId, context);
-                    final SubscriptionBase subscription = subscriptionBaseInternalApi.createSubscription(baseBundle,
-                                                                                                         eventsStreamForBaseSubscription == null ? null : eventsStreamForBaseSubscription.getSubscriptionBase(),
-                                                                                                         specifier.getPlanPhaseSpecifier(),
-                                                                                                         specifier.getOverrides(),
-                                                                                                         billingRequestedDate,
-                                                                                                         isMigrated,
-                                                                                                         context);
+                    final List<SubscriptionBaseWithAddOns> subscriptionsWithAddOns = subscriptionBaseInternalApi.createBaseSubscriptionsWithAddOns(ImmutableList.<SubscriptionBaseWithAddOnsSpecifier>of(subscriptionBaseWithAddOnsSpecifier),
+                                                                                                                                                   false,
+                                                                                                                                                   context);
+                    final SubscriptionBase subscription = subscriptionsWithAddOns.get(0).getSubscriptionBaseList().get(0);
 
                     final BlockingState newBlockingState = new DefaultBlockingState(subscription.getId(), BlockingStateType.SUBSCRIPTION, DefaultEntitlementApi.ENT_STATE_START, EntitlementService.ENTITLEMENT_SERVICE_NAME, false, false, false, entitlementRequestedDate);
                     entitlementUtils.setBlockingStatesAndPostBlockingTransitionEvent(ImmutableList.<BlockingState>of(newBlockingState), subscription.getBundleId(), context);
diff --git a/subscription/src/main/java/org/killbill/billing/subscription/api/SubscriptionBaseApiService.java b/subscription/src/main/java/org/killbill/billing/subscription/api/SubscriptionBaseApiService.java
index 2740f09..a877800 100644
--- a/subscription/src/main/java/org/killbill/billing/subscription/api/SubscriptionBaseApiService.java
+++ b/subscription/src/main/java/org/killbill/billing/subscription/api/SubscriptionBaseApiService.java
@@ -1,7 +1,7 @@
 /*
  * Copyright 2010-2013 Ning, Inc.
- * Copyright 2014-2017 Groupon, Inc
- * Copyright 2014-2017 The Billing Project, LLC
+ * Copyright 2014-2018 Groupon, Inc
+ * Copyright 2014-2018 The Billing Project, LLC
  *
  * The Billing Project licenses this file to you under the Apache License, version 2.0
  * (the "License"); you may not use this file except in compliance with the
@@ -36,18 +36,12 @@ import org.killbill.billing.catalog.api.PlanSpecifier;
 import org.killbill.billing.subscription.api.user.DefaultSubscriptionBase;
 import org.killbill.billing.subscription.api.user.SubscriptionAndAddOnsSpecifier;
 import org.killbill.billing.subscription.api.user.SubscriptionBaseApiException;
-import org.killbill.billing.subscription.api.user.SubscriptionBuilder;
 import org.killbill.billing.subscription.events.SubscriptionBaseEvent;
 import org.killbill.billing.util.callcontext.CallContext;
 import org.killbill.billing.util.callcontext.TenantContext;
 
 public interface SubscriptionBaseApiService {
 
-    public DefaultSubscriptionBase createPlan(SubscriptionBuilder builder, Plan plan, PhaseType initialPhase,
-                                              String realPriceList, DateTime effectiveDate,
-                                              Catalog catalog, CallContext context)
-            throws SubscriptionBaseApiException;
-
     public List<SubscriptionBaseWithAddOns> createPlansWithAddOns(UUID accountId, Iterable<SubscriptionAndAddOnsSpecifier> subscriptionsAndAddOns, Catalog catalog, CallContext context)
             throws SubscriptionBaseApiException;
 
@@ -98,13 +92,6 @@ public interface SubscriptionBaseApiService {
                                                            InternalTenantContext context)
             throws CatalogApiException, SubscriptionBaseApiException;
 
-    /*
-        public List<SubscriptionBaseEvent> getEventsOnChangePlan(final DefaultSubscriptionBase subscription, final Plan newPlan,
-                                                             final String newPriceList, final DateTime effectiveDate,
-                                                             final boolean addCancellationAddOnForEventsIfRequired, final InternalTenantContext internalTenantContext) throws CatalogApiException, SubscriptionBaseApiException {
-
-     */
-
     public List<SubscriptionBaseEvent> getEventsOnChangePlan(DefaultSubscriptionBase subscription, Plan newPlan,
                                                              String newPriceList, DateTime effectiveDate,
                                                              boolean addCancellationAddOnForEventsIfRequired,
diff --git a/subscription/src/main/java/org/killbill/billing/subscription/api/svcs/DefaultSubscriptionInternalApi.java b/subscription/src/main/java/org/killbill/billing/subscription/api/svcs/DefaultSubscriptionInternalApi.java
index e5b22df..5a62193 100644
--- a/subscription/src/main/java/org/killbill/billing/subscription/api/svcs/DefaultSubscriptionInternalApi.java
+++ b/subscription/src/main/java/org/killbill/billing/subscription/api/svcs/DefaultSubscriptionInternalApi.java
@@ -47,7 +47,6 @@ import org.killbill.billing.catalog.api.PlanPhasePriceOverride;
 import org.killbill.billing.catalog.api.PlanPhasePriceOverridesWithCallContext;
 import org.killbill.billing.catalog.api.PlanPhaseSpecifier;
 import org.killbill.billing.catalog.api.ProductCategory;
-import org.killbill.billing.entitlement.api.BaseEntitlementWithAddOnsSpecifier;
 import org.killbill.billing.entitlement.api.Entitlement.EntitlementState;
 import org.killbill.billing.entitlement.api.EntitlementAOStatusDryRun;
 import org.killbill.billing.entitlement.api.EntitlementAOStatusDryRun.DryRunChangeReason;
@@ -59,6 +58,7 @@ import org.killbill.billing.subscription.api.SubscriptionBase;
 import org.killbill.billing.subscription.api.SubscriptionBaseApiService;
 import org.killbill.billing.subscription.api.SubscriptionBaseInternalApi;
 import org.killbill.billing.subscription.api.SubscriptionBaseWithAddOns;
+import org.killbill.billing.subscription.api.SubscriptionBaseWithAddOnsSpecifier;
 import org.killbill.billing.subscription.api.user.DefaultEffectiveSubscriptionEvent;
 import org.killbill.billing.subscription.api.user.DefaultSubscriptionBase;
 import org.killbill.billing.subscription.api.user.DefaultSubscriptionBaseBundle;
@@ -136,70 +136,27 @@ public class DefaultSubscriptionInternalApi extends SubscriptionApiBase implemen
         this.catalogInternalApi = catalogInternalApi;
     }
 
-    @Override
-    public SubscriptionBase createSubscription(final SubscriptionBaseBundle bundle,
-                                               @Nullable final SubscriptionBase baseSubscription,
-                                               final PlanPhaseSpecifier spec,
-                                               final List<PlanPhasePriceOverride> overrides,
-                                               final DateTime requestedDateWithMs,
-                                               final boolean isMigrated,
-                                               final InternalCallContext context) throws SubscriptionBaseApiException {
-        try {
-            if (bundle == null) {
-                throw new SubscriptionBaseApiException(ErrorCode.SUB_CREATE_NO_BUNDLE, null);
-            }
-
-            final DateTime now = clock.getUTCNow();
-            final DateTime effectiveDate = (requestedDateWithMs != null) ? DefaultClock.truncateMs(requestedDateWithMs) : now;
-            /*
-            if (requestedDate.isAfter(now)) {
-                throw new SubscriptionBaseApiException(ErrorCode.SUB_INVALID_REQUESTED_DATE, now.toString(), requestedDate.toString());
-            }
-            */
-
-            final CallContext callContext = internalCallContextFactory.createCallContext(context);
-            final Catalog catalog = catalogInternalApi.getFullCatalog(true, true, context);
-            final PlanPhasePriceOverridesWithCallContext overridesWithContext = new DefaultPlanPhasePriceOverridesWithCallContext(overrides, callContext);
-
-            final Plan plan = catalog.createOrFindPlan(spec, overridesWithContext, effectiveDate);
-            final PlanPhase phase = plan.getAllPhases()[0];
-            if (phase == null) {
-                throw new SubscriptionBaseError(String.format("No initial PlanPhase for Product %s, term %s and set %s does not exist in the catalog",
-                                                              spec.getProductName(), spec.getBillingPeriod().toString(), plan.getPriceListName()));
-            }
-
-            // verify the number of subscriptions (of the same kind) allowed per bundle
-            if (ProductCategory.ADD_ON.toString().equalsIgnoreCase(plan.getProduct().getCategory().toString())) {
-                if (plan.getPlansAllowedInBundle() != -1
-                    && plan.getPlansAllowedInBundle() > 0
-                    && addonUtils.countExistingAddOnsWithSamePlanName(getSubscriptionsForBundle(bundle.getId(), null, context), plan.getName())
-                       >= plan.getPlansAllowedInBundle()) {
-                    // a new ADD_ON subscription of the same plan can't be added because it has reached its limit by bundle
-                    throw new SubscriptionBaseApiException(ErrorCode.SUB_CREATE_AO_MAX_PLAN_ALLOWED_BY_BUNDLE, plan.getName());
-                }
+    private List<SubscriptionSpecifier> verifyAndBuildSubscriptionSpecifiers(final SubscriptionBaseBundle bundle,
+                                                                             @Nullable final EntitlementSpecifier baseOrStandalonePlanSpecifier,
+                                                                             final Iterable<EntitlementSpecifier> entitlements,
+                                                                             final boolean isMigrated,
+                                                                             final InternalCallContext context,
+                                                                             final DateTime now,
+                                                                             final DateTime effectiveDate,
+                                                                             final Catalog catalog,
+                                                                             final CallContext callContext) throws SubscriptionBaseApiException, CatalogApiException {
+        final List<SubscriptionSpecifier> subscriptions = new ArrayList<SubscriptionSpecifier>();
+        // TODO We should also look to the specifiers being created for validation
+        final List<SubscriptionBase> subscriptionsForBundle = getSubscriptionsForBundle(bundle.getId(), null, context);
+        SubscriptionBase baseSubscription = null;
+        for (final SubscriptionBase cur : subscriptionsForBundle) {
+            if (cur.getCategory() == ProductCategory.BASE) {
+                baseSubscription = cur;
+                break;
             }
-
-            final DateTime bundleStartDate = getBundleStartDateWithSanity(bundle.getId(), baseSubscription, plan, effectiveDate, catalog, context);
-            return apiService.createPlan(new SubscriptionBuilder()
-                                                 .setId(UUIDs.randomUUID())
-                                                 .setBundleId(bundle.getId())
-                                                 .setBundleExternalKey(bundle.getExternalKey())
-                                                 .setCategory(plan.getProduct().getCategory())
-                                                 .setBundleStartDate(bundleStartDate)
-                                                 .setAlignStartDate(effectiveDate)
-                                                 .setMigrated(isMigrated),
-                                         plan, spec.getPhaseType(), plan.getPriceListName(), effectiveDate,  catalog, callContext);
-        } catch (final CatalogApiException e) {
-            throw new SubscriptionBaseApiException(e);
         }
-    }
-
-    private List<SubscriptionSpecifier> verifyAndBuildSubscriptionSpecifiers(final UUID bundleId, final String externalKey, final Iterable<EntitlementSpecifier> entitlements, final boolean isMigrated, final InternalCallContext context, final DateTime now, final DateTime effectiveDate, final Catalog catalog, final CallContext callContext) throws SubscriptionBaseApiException, CatalogApiException {
-        final List<SubscriptionSpecifier> subscriptions = new ArrayList<SubscriptionSpecifier>();
-        final List<SubscriptionBase> subscriptionsForBundle = getSubscriptionsForBundle(bundleId, null, context);
 
         for (final EntitlementSpecifier entitlement : entitlements) {
-
             final PlanPhaseSpecifier spec = entitlement.getPlanPhaseSpecifier();
             if (spec == null) {
                 // BP already exists
@@ -227,6 +184,8 @@ public class DefaultSubscriptionInternalApi extends SubscriptionApiBase implemen
                 }
             }
 
+            final DateTime bundleStartDate = baseOrStandalonePlanSpecifier != null ? effectiveDate : getBundleStartDateWithSanity(bundle.getId(), baseSubscription, plan, effectiveDate, catalog, context);
+
             final SubscriptionSpecifier subscription = new SubscriptionSpecifier();
             subscription.setRealPriceList(plan.getPriceListName());
             subscription.setEffectiveDate(effectiveDate);
@@ -235,10 +194,10 @@ public class DefaultSubscriptionInternalApi extends SubscriptionApiBase implemen
             subscription.setInitialPhase(spec.getPhaseType());
             subscription.setBuilder(new SubscriptionBuilder()
                                             .setId(UUIDs.randomUUID())
-                                            .setBundleId(bundleId)
-                                            .setBundleExternalKey(externalKey)
+                                            .setBundleId(bundle.getId())
+                                            .setBundleExternalKey(bundle.getExternalKey())
                                             .setCategory(plan.getProduct().getCategory())
-                                            .setBundleStartDate(effectiveDate)
+                                            .setBundleStartDate(bundleStartDate)
                                             .setAlignStartDate(effectiveDate)
                                             .setMigrated(isMigrated));
 
@@ -247,17 +206,22 @@ public class DefaultSubscriptionInternalApi extends SubscriptionApiBase implemen
         return subscriptions;
     }
 
-    private boolean sanityAndReorderBPOrStandaloneSpecFirst(final Catalog catalog, final BaseEntitlementWithAddOnsSpecifier entitlementWithAddOnsSpecifier, final DateTime effectiveDate, final List<EntitlementSpecifier> outputEntitlementSpecifier) throws SubscriptionBaseApiException {
-
+    private EntitlementSpecifier sanityAndReorderBPOrStandaloneSpecFirst(final Catalog catalog,
+                                                                         final SubscriptionBaseWithAddOnsSpecifier subscriptionBaseWithAddOnsSpecifier,
+                                                                         final DateTime effectiveDate,
+                                                                         final Collection<EntitlementSpecifier> outputEntitlementSpecifier) throws SubscriptionBaseApiException {
         EntitlementSpecifier baseOrStandalonePlanSpecifier = null;
-        final List<EntitlementSpecifier> addOnSpecifiers = new ArrayList<EntitlementSpecifier>();
+        final Collection<EntitlementSpecifier> addOnSpecifiers = new ArrayList<EntitlementSpecifier>();
         try {
-            for (final EntitlementSpecifier cur : entitlementWithAddOnsSpecifier.getEntitlementSpecifier()) {
-                final Plan inputPlan = catalog.createOrFindPlan(cur.getPlanPhaseSpecifier(), null, effectiveDate);
-                final boolean isBaseOrStandaloneSpecifier = inputPlan.getProduct().getCategory() == ProductCategory.BASE || inputPlan.getProduct().getCategory() == ProductCategory.STANDALONE;
+            for (final EntitlementSpecifier cur : subscriptionBaseWithAddOnsSpecifier.getEntitlementSpecifiers()) {
+                final boolean isBase = isBaseSpecifier(catalog, effectiveDate, cur);
+                final boolean isStandalone = isStandaloneSpecifier(catalog, effectiveDate, cur);
+                final boolean isBaseOrStandaloneSpecifier = isBase || isStandalone;
                 if (isBaseOrStandaloneSpecifier) {
-                    if (baseOrStandalonePlanSpecifier == null) {
+                    if (baseOrStandalonePlanSpecifier == null &&
+                        (isStandalone || (isBase && subscriptionBaseWithAddOnsSpecifier.getBundleId() == null))) {
                         baseOrStandalonePlanSpecifier = cur;
+
                     } else {
                         throw new SubscriptionBaseApiException(ErrorCode.SUB_CREATE_INVALID_ENTITLEMENT_SPECIFIER);
                     }
@@ -273,48 +237,84 @@ public class DefaultSubscriptionInternalApi extends SubscriptionApiBase implemen
             outputEntitlementSpecifier.add(baseOrStandalonePlanSpecifier);
         }
         outputEntitlementSpecifier.addAll(addOnSpecifiers);
-        return baseOrStandalonePlanSpecifier != null;
+        return baseOrStandalonePlanSpecifier;
+    }
+
+    private boolean isBaseSpecifier(final Catalog catalog, final DateTime effectiveDate, final EntitlementSpecifier cur) throws CatalogApiException {
+        final Plan inputPlan = catalog.createOrFindPlan(cur.getPlanPhaseSpecifier(), null, effectiveDate);
+        return inputPlan.getProduct().getCategory() == ProductCategory.BASE;
+    }
+
+    private boolean isStandaloneSpecifier(final Catalog catalog, final DateTime effectiveDate, final EntitlementSpecifier cur) throws CatalogApiException {
+        final Plan inputPlan = catalog.createOrFindPlan(cur.getPlanPhaseSpecifier(), null, effectiveDate);
+        return inputPlan.getProduct().getCategory() == ProductCategory.STANDALONE;
     }
 
     @Override
-    public List<SubscriptionBaseWithAddOns> createBaseSubscriptionsWithAddOns(final UUID accountId, final Iterable<BaseEntitlementWithAddOnsSpecifier> baseEntitlementWithAddOnsSpecifier, final boolean renameCancelledBundleIfExist, final InternalCallContext context) throws SubscriptionBaseApiException {
+    public List<SubscriptionBaseWithAddOns> createBaseSubscriptionsWithAddOns(final Iterable<SubscriptionBaseWithAddOnsSpecifier> subscriptionWithAddOnsSpecifiers, final boolean renameCancelledBundleIfExist, final InternalCallContext context) throws SubscriptionBaseApiException {
         try {
             final Catalog catalog = catalogInternalApi.getFullCatalog(true, true, context);
             final CallContext callContext = internalCallContextFactory.createCallContext(context);
+            final UUID accountId = callContext.getAccountId();
             final DateTime now = clock.getUTCNow();
 
             final Collection<SubscriptionAndAddOnsSpecifier> subscriptionAndAddOns = new ArrayList<SubscriptionAndAddOnsSpecifier>();
-            for (final BaseEntitlementWithAddOnsSpecifier entitlementWithAddOnsSpecifier : baseEntitlementWithAddOnsSpecifier) {
-                final DateTime effectiveDate = (entitlementWithAddOnsSpecifier.getBillingEffectiveDate() != null) ?
-                                               context.toUTCDateTime(entitlementWithAddOnsSpecifier.getBillingEffectiveDate()) : now;
-
-                final List<EntitlementSpecifier> reorderedSpecifiers = new ArrayList<EntitlementSpecifier>();
-                final boolean isBaseOrStandaloneSpecifierExists = sanityAndReorderBPOrStandaloneSpecFirst(catalog, entitlementWithAddOnsSpecifier, effectiveDate, reorderedSpecifiers);
-
-                final SubscriptionBaseBundle bundle;
-                if (isBaseOrStandaloneSpecifierExists) {
-                    bundle = createBundleForAccount(accountId, entitlementWithAddOnsSpecifier.getExternalKey(), renameCancelledBundleIfExist, context);
-                } else {
-                    final List<SubscriptionBaseBundle> existingBundles = dao.getSubscriptionBundlesForKey(entitlementWithAddOnsSpecifier.getExternalKey(), context);
+            for (final SubscriptionBaseWithAddOnsSpecifier subscriptionBaseWithAddOnsSpecifier : subscriptionWithAddOnsSpecifiers) {
+                final DateTime billingRequestedDateRaw = subscriptionBaseWithAddOnsSpecifier.getBillingEffectiveDate() != null ? subscriptionBaseWithAddOnsSpecifier.getBillingEffectiveDate() : now;
+
+                final Collection<EntitlementSpecifier> reorderedSpecifiers = new ArrayList<EntitlementSpecifier>();
+                // Note: billingRequestedDateRaw might not be accurate here (add-on with a too early date passed)?
+                final EntitlementSpecifier baseOrStandalonePlanSpecifier = sanityAndReorderBPOrStandaloneSpecFirst(catalog, subscriptionBaseWithAddOnsSpecifier, billingRequestedDateRaw, reorderedSpecifiers);
+
+                DateTime billingRequestedDate = billingRequestedDateRaw;
+                SubscriptionBaseBundle bundle = null;
+                if (subscriptionBaseWithAddOnsSpecifier.getBundleId() != null) {
+                    bundle = dao.getSubscriptionBundleFromId(subscriptionBaseWithAddOnsSpecifier.getBundleId(), context);
+                    if (bundle == null || (subscriptionBaseWithAddOnsSpecifier.getBundleExternalKey() != null && subscriptionBaseWithAddOnsSpecifier.getBundleExternalKey().equals(bundle.getExternalKey()))) {
+                        throw new SubscriptionBaseApiException(ErrorCode.SUB_CREATE_INVALID_ENTITLEMENT_SPECIFIER);
+                    }
+                } else if (subscriptionBaseWithAddOnsSpecifier.getBundleExternalKey() != null) {
+                    final List<SubscriptionBaseBundle> existingBundles = dao.getSubscriptionBundlesForKey(subscriptionBaseWithAddOnsSpecifier.getBundleExternalKey(), context);
                     final SubscriptionBaseBundle tmp = getActiveBundleForKeyNotException(existingBundles, dao, clock, catalog, context);
                     if (tmp == null) {
-                        throw new SubscriptionBaseApiException(ErrorCode.SUB_CREATE_NO_BP, entitlementWithAddOnsSpecifier.getExternalKey());
+                        if (baseOrStandalonePlanSpecifier == null) {
+                            throw new SubscriptionBaseApiException(ErrorCode.SUB_CREATE_NO_BP, subscriptionBaseWithAddOnsSpecifier.getBundleExternalKey());
+                        }
                     } else if (!tmp.getAccountId().equals(accountId)) {
-                        throw new SubscriptionBaseApiException(ErrorCode.SUB_CREATE_ACTIVE_BUNDLE_KEY_EXISTS, entitlementWithAddOnsSpecifier.getExternalKey());
+                        throw new SubscriptionBaseApiException(ErrorCode.SUB_CREATE_ACTIVE_BUNDLE_KEY_EXISTS, subscriptionBaseWithAddOnsSpecifier.getBundleExternalKey());
                     } else {
                         bundle = tmp;
                     }
                 }
 
+                if (bundle != null) {
+                    final SubscriptionBase baseSubscription = dao.getBaseSubscription(bundle.getId(), catalog, context);
+                    if (baseSubscription != null) {
+                        final DateTime baseSubscriptionStartDate = getBaseSubscription(bundle.getId(), context).getStartDate();
+                        billingRequestedDate = billingRequestedDateRaw.isBefore(baseSubscriptionStartDate) ? baseSubscriptionStartDate : billingRequestedDateRaw;
+                    }
+                }
+
+                if (bundle == null && baseOrStandalonePlanSpecifier != null) {
+                    bundle = createBundleForAccount(accountId,
+                                                    subscriptionBaseWithAddOnsSpecifier.getBundleExternalKey(),
+                                                    renameCancelledBundleIfExist,
+                                                    context);
+                } else if (bundle != null && baseOrStandalonePlanSpecifier != null && isBaseSpecifier(catalog, billingRequestedDateRaw, baseOrStandalonePlanSpecifier)) {
+                    throw new SubscriptionBaseApiException(ErrorCode.SUB_CREATE_BP_EXISTS, bundle.getExternalKey());
+                } else if (bundle == null) {
+                    throw new SubscriptionBaseApiException(ErrorCode.SUB_CREATE_INVALID_ENTITLEMENT_SPECIFIER);
+                }
+
                 final SubscriptionAndAddOnsSpecifier subscriptionAndAddOnsSpecifier = new SubscriptionAndAddOnsSpecifier(bundle,
-                                                                                                                         effectiveDate,
-                                                                                                                         verifyAndBuildSubscriptionSpecifiers(bundle.getId(),
-                                                                                                                                                              bundle.getExternalKey(),
+                                                                                                                         billingRequestedDate,
+                                                                                                                         verifyAndBuildSubscriptionSpecifiers(bundle,
+                                                                                                                                                              baseOrStandalonePlanSpecifier,
                                                                                                                                                               reorderedSpecifiers,
-                                                                                                                                                              entitlementWithAddOnsSpecifier.isMigrated(),
+                                                                                                                                                              subscriptionBaseWithAddOnsSpecifier.isMigrated(),
                                                                                                                                                               context,
                                                                                                                                                               now,
-                                                                                                                                                              effectiveDate,
+                                                                                                                                                              billingRequestedDate,
                                                                                                                                                               catalog,
                                                                                                                                                               callContext));
                 subscriptionAndAddOns.add(subscriptionAndAddOnsSpecifier);
@@ -371,9 +371,9 @@ public class DefaultSubscriptionInternalApi extends SubscriptionApiBase implemen
 
     }
 
+    @VisibleForTesting
     @Override
     public SubscriptionBaseBundle createBundleForAccount(final UUID accountId, final String bundleKey, final boolean renameCancelledBundleIfExist, final InternalCallContext context) throws SubscriptionBaseApiException {
-
         final DateTime now = clock.getUTCNow();
         final DefaultSubscriptionBaseBundle bundle = new DefaultSubscriptionBaseBundle(bundleKey, accountId, now, now, now, now);
         if (null != bundleKey && bundleKey.length() > 255) {
diff --git a/subscription/src/main/java/org/killbill/billing/subscription/api/user/DefaultSubscriptionBaseApiService.java b/subscription/src/main/java/org/killbill/billing/subscription/api/user/DefaultSubscriptionBaseApiService.java
index 1e1fc4e..3d9a13b 100644
--- a/subscription/src/main/java/org/killbill/billing/subscription/api/user/DefaultSubscriptionBaseApiService.java
+++ b/subscription/src/main/java/org/killbill/billing/subscription/api/user/DefaultSubscriptionBaseApiService.java
@@ -102,26 +102,6 @@ public class DefaultSubscriptionBaseApiService implements SubscriptionBaseApiSer
     }
 
     @Override
-    public DefaultSubscriptionBase createPlan(final SubscriptionBuilder builder, final Plan plan, final PhaseType initialPhase,
-                                              final String realPriceList, final DateTime effectiveDate, final Catalog fullCatalog,
-                                              final CallContext context) throws SubscriptionBaseApiException {
-        final DefaultSubscriptionBase subscription = new DefaultSubscriptionBase(builder, this, clock);
-
-        final InternalCallContext internalCallContext = createCallContextFromBundleId(subscription.getBundleId(), context);
-
-        try {
-
-            final List<SubscriptionBaseEvent> events = getEventsOnCreation(subscription.getId(), subscription.getAlignStartDate(), subscription.getBundleStartDate(),
-                                                                           plan, initialPhase, realPriceList, effectiveDate, fullCatalog, internalCallContext);
-            final List<SubscriptionBaseEvent> createdEvents = dao.createSubscription(subscription, events, fullCatalog, internalCallContext);
-            subscription.rebuildTransitions(createdEvents, fullCatalog);
-            return subscription;
-        } catch (final CatalogApiException e) {
-            throw new SubscriptionBaseApiException(e);
-        }
-    }
-
-    @Override
     public List<SubscriptionBaseWithAddOns> createPlansWithAddOns(final UUID accountId, final Iterable<SubscriptionAndAddOnsSpecifier> subscriptionsAndAddOns, final Catalog fullCatalog, final CallContext context) throws SubscriptionBaseApiException {
         final Map<UUID, List<SubscriptionBaseEvent>> eventsMap = new HashMap<UUID, List<SubscriptionBaseEvent>>();
         final Collection<List<SubscriptionBase>> subscriptionBaseAndAddOnsList = new ArrayList<List<SubscriptionBase>>();
diff --git a/subscription/src/main/java/org/killbill/billing/subscription/engine/addon/AddonUtils.java b/subscription/src/main/java/org/killbill/billing/subscription/engine/addon/AddonUtils.java
index b88eb57..587e1b7 100644
--- a/subscription/src/main/java/org/killbill/billing/subscription/engine/addon/AddonUtils.java
+++ b/subscription/src/main/java/org/killbill/billing/subscription/engine/addon/AddonUtils.java
@@ -128,9 +128,9 @@ public class AddonUtils {
         return false;
     }
 
-    public int countExistingAddOnsWithSamePlanName(final List<SubscriptionBase> subscriptionsForBundle, final String planName) {
+    public int countExistingAddOnsWithSamePlanName(final Iterable<SubscriptionBase> subscriptionsForBundle, final String planName) {
         int countExistingAddOns = 0;
-        for (SubscriptionBase subscription : subscriptionsForBundle) {
+        for (final SubscriptionBase subscription : subscriptionsForBundle) {
             if (subscription.getCurrentPlan().getName().equalsIgnoreCase(planName)
                 && subscription.getLastActiveProduct().getCategory() != null
                 && ProductCategory.ADD_ON.equals(subscription.getLastActiveProduct().getCategory())) {
diff --git a/subscription/src/main/java/org/killbill/billing/subscription/engine/dao/DefaultSubscriptionDao.java b/subscription/src/main/java/org/killbill/billing/subscription/engine/dao/DefaultSubscriptionDao.java
index d0c740e..f7c8fd0 100644
--- a/subscription/src/main/java/org/killbill/billing/subscription/engine/dao/DefaultSubscriptionDao.java
+++ b/subscription/src/main/java/org/killbill/billing/subscription/engine/dao/DefaultSubscriptionDao.java
@@ -550,40 +550,14 @@ public class DefaultSubscriptionDao extends EntityDaoBase<SubscriptionBundleMode
     }
 
     @Override
-    public final List<SubscriptionBaseEvent> createSubscription(final DefaultSubscriptionBase subscription, final List<SubscriptionBaseEvent> initialEvents, final Catalog catalog, final InternalCallContext context) {
+    public List<SubscriptionBaseEvent> createSubscriptionsWithAddOns(final List<SubscriptionBaseWithAddOns> subscriptions, final Map<UUID, List<SubscriptionBaseEvent>> initialEventsMap, final Catalog catalog, final InternalCallContext context) {
         return transactionalSqlDao.execute(false, new EntitySqlDaoTransactionWrapper<List<SubscriptionBaseEvent>>() {
             @Override
             public List<SubscriptionBaseEvent> inTransaction(final EntitySqlDaoWrapperFactory entitySqlDaoWrapperFactory) throws Exception {
                 final SubscriptionSqlDao transactional = entitySqlDaoWrapperFactory.become(SubscriptionSqlDao.class);
-                createAndRefresh(transactional, new SubscriptionModelDao(subscription), context);
-
-                final List<SubscriptionEventModelDao> createdEvents = new LinkedList<SubscriptionEventModelDao>();
-                final SubscriptionEventSqlDao eventsDaoFromSameTransaction = entitySqlDaoWrapperFactory.become(SubscriptionEventSqlDao.class);
-                for (final SubscriptionBaseEvent cur : initialEvents) {
-                    createdEvents.add(createAndRefresh(eventsDaoFromSameTransaction, new SubscriptionEventModelDao(cur), context));
-
-                    final boolean isBusEvent = cur.getEffectiveDate().compareTo(clock.getUTCNow()) <= 0 && (cur.getType() == EventType.API_USER);
-                    recordBusOrFutureNotificationFromTransaction(subscription, cur, entitySqlDaoWrapperFactory, isBusEvent, 0, catalog, context);
-
-                }
-                // Notify the Bus of the latest requested change, if needed
-                if (!initialEvents.isEmpty()) {
-                    notifyBusOfRequestedChange(entitySqlDaoWrapperFactory, subscription, initialEvents.get(initialEvents.size() - 1), SubscriptionBaseTransitionType.CREATE, context);
-                }
-
-                return toSubscriptionBaseEvents(createdEvents);
-            }
-        });
-    }
-
-    @Override
-    public void createSubscriptionsWithAddOns(final List<SubscriptionBaseWithAddOns> subscriptions, final Map<UUID, List<SubscriptionBaseEvent>> initialEventsMap, final Catalog catalog, final InternalCallContext context) {
-        transactionalSqlDao.execute(false, new EntitySqlDaoTransactionWrapper<Void>() {
-            @Override
-            public Void inTransaction(final EntitySqlDaoWrapperFactory entitySqlDaoWrapperFactory) throws Exception {
-                final SubscriptionSqlDao transactional = entitySqlDaoWrapperFactory.become(SubscriptionSqlDao.class);
                 final SubscriptionEventSqlDao eventsDaoFromSameTransaction = entitySqlDaoWrapperFactory.become(SubscriptionEventSqlDao.class);
 
+                final List<SubscriptionEventModelDao> createdEvents = new LinkedList<SubscriptionEventModelDao>();
                 for (final SubscriptionBaseWithAddOns subscription : subscriptions) {
                     for (final SubscriptionBase subscriptionBase : subscription.getSubscriptionBaseList()) {
                         // Safe cast
@@ -592,7 +566,7 @@ public class DefaultSubscriptionDao extends EntityDaoBase<SubscriptionBundleMode
 
                         final List<SubscriptionBaseEvent> initialEvents = initialEventsMap.get(defaultSubscriptionBase.getId());
                         for (final SubscriptionBaseEvent cur : initialEvents) {
-                            createAndRefresh(eventsDaoFromSameTransaction, new SubscriptionEventModelDao(cur), context);
+                            createdEvents.add(createAndRefresh(eventsDaoFromSameTransaction, new SubscriptionEventModelDao(cur), context));
 
                             final boolean isBusEvent = cur.getEffectiveDate().compareTo(clock.getUTCNow()) <= 0 && (cur.getType() == EventType.API_USER);
                             recordBusOrFutureNotificationFromTransaction(defaultSubscriptionBase, cur, entitySqlDaoWrapperFactory, isBusEvent, 0, catalog, context);
@@ -604,7 +578,7 @@ public class DefaultSubscriptionDao extends EntityDaoBase<SubscriptionBundleMode
                         }
                     }
                 }
-                return null;
+                return toSubscriptionBaseEvents(createdEvents);
             }
         });
     }
diff --git a/subscription/src/main/java/org/killbill/billing/subscription/engine/dao/SubscriptionDao.java b/subscription/src/main/java/org/killbill/billing/subscription/engine/dao/SubscriptionDao.java
index 5c623ee..8a86d46 100644
--- a/subscription/src/main/java/org/killbill/billing/subscription/engine/dao/SubscriptionDao.java
+++ b/subscription/src/main/java/org/killbill/billing/subscription/engine/dao/SubscriptionDao.java
@@ -82,9 +82,8 @@ public interface SubscriptionDao extends EntityDao<SubscriptionBundleModelDao, S
     public List<SubscriptionBaseEvent> getPendingEventsForSubscription(UUID subscriptionId, InternalTenantContext context);
 
     // SubscriptionBase creation, cancellation, changePlanWithRequestedDate apis
-    public List<SubscriptionBaseEvent> createSubscription(DefaultSubscriptionBase subscription, List<SubscriptionBaseEvent> initialEvents, final Catalog catalog, InternalCallContext context);
 
-    public void createSubscriptionsWithAddOns(List<SubscriptionBaseWithAddOns> subscriptions, Map<UUID, List<SubscriptionBaseEvent>> initialEventsMap, final Catalog catalog, InternalCallContext context);
+    public List<SubscriptionBaseEvent> createSubscriptionsWithAddOns(List<SubscriptionBaseWithAddOns> subscriptions, Map<UUID, List<SubscriptionBaseEvent>> initialEventsMap, final Catalog catalog, InternalCallContext context);
 
     public void cancelSubscriptionsOnBasePlanEvent(DefaultSubscriptionBase subscription, SubscriptionBaseEvent event, List<DefaultSubscriptionBase> subscriptions, List<SubscriptionBaseEvent> cancelEvents, final Catalog catalog, InternalCallContext context);
 
diff --git a/subscription/src/main/java/org/killbill/billing/subscription/engine/dao/SubscriptionEventSqlDao.java b/subscription/src/main/java/org/killbill/billing/subscription/engine/dao/SubscriptionEventSqlDao.java
index 0e195db..be4ddd1 100644
--- a/subscription/src/main/java/org/killbill/billing/subscription/engine/dao/SubscriptionEventSqlDao.java
+++ b/subscription/src/main/java/org/killbill/billing/subscription/engine/dao/SubscriptionEventSqlDao.java
@@ -1,7 +1,9 @@
 /*
  * Copyright 2010-2013 Ning, Inc.
+ * Copyright 2014-2018 Groupon, Inc
+ * Copyright 2014-2018 The Billing Project, LLC
  *
- * Ning licenses this file to you under the Apache License, version 2.0
+ * The Billing Project licenses this file to you under the Apache License, version 2.0
  * (the "License"); you may not use this file except in compliance with the
  * License.  You may obtain a copy of the License at:
  *
@@ -26,9 +28,9 @@ import org.killbill.billing.subscription.events.SubscriptionBaseEvent;
 import org.killbill.billing.util.audit.ChangeType;
 import org.killbill.billing.util.entity.dao.Audited;
 import org.killbill.billing.util.entity.dao.EntitySqlDao;
+import org.killbill.commons.jdbi.binder.SmartBindBean;
 import org.killbill.commons.jdbi.template.KillBillSqlDaoStringTemplate;
 import org.skife.jdbi.v2.sqlobject.Bind;
-import org.killbill.commons.jdbi.binder.SmartBindBean;
 import org.skife.jdbi.v2.sqlobject.SqlQuery;
 import org.skife.jdbi.v2.sqlobject.SqlUpdate;
 
@@ -53,7 +55,4 @@ public interface SubscriptionEventSqlDao extends EntitySqlDao<SubscriptionEventM
     @SqlQuery
     public List<SubscriptionEventModelDao> getActiveEventsForSubscription(@Bind("subscriptionId") String subscriptionId,
                                                                           @SmartBindBean final InternalTenantContext context);
-
-    @SqlQuery
-    public List<SubscriptionEventModelDao> getFutureActiveEventsForAccount(@Bind("now") Date now, @SmartBindBean final InternalTenantContext context);
 }
diff --git a/subscription/src/main/resources/org/killbill/billing/subscription/engine/dao/SubscriptionEventSqlDao.sql.stg b/subscription/src/main/resources/org/killbill/billing/subscription/engine/dao/SubscriptionEventSqlDao.sql.stg
index 94d407f..b8399da 100644
--- a/subscription/src/main/resources/org/killbill/billing/subscription/engine/dao/SubscriptionEventSqlDao.sql.stg
+++ b/subscription/src/main/resources/org/killbill/billing/subscription/engine/dao/SubscriptionEventSqlDao.sql.stg
@@ -12,7 +12,6 @@ defaultOrderBy(prefix) ::= <<
 order by <prefix>effective_date ASC, <recordIdField(prefix)> ASC
 >>
 
-
 tableFields(prefix) ::= <<
   <prefix> event_type
 , <prefix> user_type
@@ -96,17 +95,5 @@ and is_active = true
 ;
 >>
 
-getFutureActiveEventsForAccount() ::= <<
-select <allTableFields("")>
-, record_id as total_ordering
-from <tableName()>
-where
-account_record_id = :accountRecordId
-and is_active = true
-and effective_date > :now
-<AND_CHECK_TENANT("")>
-<defaultOrderBy("")>
-;
->>
 
 
diff --git a/subscription/src/test/java/org/killbill/billing/subscription/api/user/TestSubscriptionHelper.java b/subscription/src/test/java/org/killbill/billing/subscription/api/user/TestSubscriptionHelper.java
index 9c67ec3..87a882a 100644
--- a/subscription/src/test/java/org/killbill/billing/subscription/api/user/TestSubscriptionHelper.java
+++ b/subscription/src/test/java/org/killbill/billing/subscription/api/user/TestSubscriptionHelper.java
@@ -1,7 +1,7 @@
 /*
  * Copyright 2010-2013 Ning, Inc.
- * Copyright 2014-2015 Groupon, Inc
- * Copyright 2014-2015 The Billing Project, LLC
+ * Copyright 2014-2018 Groupon, Inc
+ * Copyright 2014-2018 The Billing Project, LLC
  *
  * The Billing Project licenses this file to you under the Apache License, version 2.0
  * (the "License"); you may not use this file except in compliance with the
@@ -28,7 +28,7 @@ import javax.inject.Inject;
 import org.joda.time.DateTime;
 import org.joda.time.LocalDate;
 import org.joda.time.Period;
-import org.killbill.billing.account.api.AccountUserApi;
+import org.killbill.billing.ObjectType;
 import org.killbill.billing.api.TestApiListener;
 import org.killbill.billing.api.TestApiListener.NextEvent;
 import org.killbill.billing.callcontext.InternalCallContext;
@@ -38,23 +38,28 @@ import org.killbill.billing.catalog.api.Duration;
 import org.killbill.billing.catalog.api.PhaseType;
 import org.killbill.billing.catalog.api.PlanPhasePriceOverride;
 import org.killbill.billing.catalog.api.PlanPhaseSpecifier;
-import org.killbill.billing.catalog.api.ProductCategory;
 import org.killbill.billing.catalog.api.TimeUnit;
+import org.killbill.billing.dao.MockNonEntityDao;
+import org.killbill.billing.entitlement.api.EntitlementSpecifier;
 import org.killbill.billing.entitlement.api.SubscriptionEventType;
 import org.killbill.billing.invoice.api.DryRunArguments;
 import org.killbill.billing.invoice.api.DryRunType;
 import org.killbill.billing.subscription.api.SubscriptionBase;
 import org.killbill.billing.subscription.api.SubscriptionBaseInternalApi;
+import org.killbill.billing.subscription.api.SubscriptionBaseWithAddOns;
+import org.killbill.billing.subscription.api.SubscriptionBaseWithAddOnsSpecifier;
 import org.killbill.billing.subscription.engine.dao.SubscriptionDao;
 import org.killbill.billing.subscription.events.SubscriptionBaseEvent;
 import org.killbill.billing.subscription.events.phase.PhaseEvent;
 import org.killbill.billing.subscription.events.user.ApiEvent;
 import org.killbill.billing.subscription.events.user.ApiEventType;
-import org.killbill.billing.util.callcontext.CallContext;
+import org.killbill.billing.util.callcontext.InternalCallContextFactory;
 import org.killbill.clock.Clock;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import com.google.common.collect.ImmutableList;
+
 import static org.testng.Assert.assertEquals;
 import static org.testng.Assert.assertFalse;
 import static org.testng.Assert.assertNotNull;
@@ -66,49 +71,66 @@ public class TestSubscriptionHelper {
 
     private final SubscriptionBaseInternalApi subscriptionApi;
     private final Clock clock;
+    private final MockNonEntityDao mockNonEntityDao;
     private final InternalCallContext internalCallContext;
     private final TestApiListener testListener;
     private final SubscriptionDao dao;
+    private final InternalCallContextFactory internalCallContextFactory;
 
     @Inject
-    public TestSubscriptionHelper(final SubscriptionBaseInternalApi subscriptionApi, final Clock clock, final InternalCallContext internallCallContext, final CallContext callContext, final TestApiListener testListener, final SubscriptionDao dao) {
+    public TestSubscriptionHelper(final SubscriptionBaseInternalApi subscriptionApi,
+                                  final Clock clock,
+                                  final MockNonEntityDao mockNonEntityDao,
+                                  final InternalCallContext internalCallContext,
+                                  final TestApiListener testListener,
+                                  final SubscriptionDao dao,
+                                  final InternalCallContextFactory internalCallContextFactory) {
         this.subscriptionApi = subscriptionApi;
         this.clock = clock;
-        this.internalCallContext = internallCallContext;
+        this.mockNonEntityDao = mockNonEntityDao;
+        this.internalCallContext = internalCallContext;
         this.testListener = testListener;
         this.dao = dao;
+        this.internalCallContextFactory = internalCallContextFactory;
     }
 
     public DryRunArguments createDryRunArguments(final UUID subscriptionId, final UUID bundleId, final PlanPhaseSpecifier spec, final LocalDate requestedDate, final SubscriptionEventType type, final BillingActionPolicy billingActionPolicy) {
-        return new  DryRunArguments() {
+        return new DryRunArguments() {
             @Override
             public DryRunType getDryRunType() {
                 return DryRunType.SUBSCRIPTION_ACTION;
             }
+
             @Override
             public PlanPhaseSpecifier getPlanPhaseSpecifier() {
                 return spec;
             }
+
             @Override
             public SubscriptionEventType getAction() {
                 return type;
             }
+
             @Override
             public UUID getSubscriptionId() {
                 return subscriptionId;
             }
+
             @Override
             public LocalDate getEffectiveDate() {
                 return requestedDate;
             }
+
             @Override
             public UUID getBundleId() {
                 return bundleId;
             }
+
             @Override
             public BillingActionPolicy getBillingActionPolicy() {
                 return billingActionPolicy;
             }
+
             @Override
             public List<PlanPhasePriceOverride> getPlanPhasePriceOverrides() {
                 return null;
@@ -118,32 +140,89 @@ public class TestSubscriptionHelper {
 
     public DefaultSubscriptionBase createSubscription(final SubscriptionBaseBundle bundle, final String productName, final BillingPeriod term, final String planSet, final DateTime requestedDate)
             throws SubscriptionBaseApiException {
-        return createSubscriptionWithBundle(bundle, null, productName, term, planSet, requestedDate);
+        return createSubscription(bundle, productName, term, planSet, null, requestedDate);
+    }
+
+    public DefaultSubscriptionBase createSubscription(final SubscriptionBaseBundle bundle, final String productName, final BillingPeriod term, final String planSet, final PhaseType phaseType, final DateTime requestedDate)
+            throws SubscriptionBaseApiException {
+        return createSubscription(bundle, null, productName, term, planSet, phaseType, requestedDate);
     }
 
     public DefaultSubscriptionBase createSubscription(final SubscriptionBaseBundle bundle, final SubscriptionBase baseSubscription, final String aoProduct, final BillingPeriod aoTerm, final String aoPriceList) throws SubscriptionBaseApiException {
-        return createSubscriptionWithBundle(bundle, baseSubscription, aoProduct, aoTerm, aoPriceList, null);
+        return createSubscription(bundle, baseSubscription, aoProduct, aoTerm, aoPriceList, null, null);
     }
 
     public DefaultSubscriptionBase createSubscription(final SubscriptionBaseBundle bundle, final String productName, final BillingPeriod term, final String planSet)
             throws SubscriptionBaseApiException {
-        return createSubscriptionWithBundle(bundle, null, productName, term, planSet, null);
+        return createSubscription(bundle, null, productName, term, planSet, null, null);
+    }
+
+    public DefaultSubscriptionBase createSubscription(final SubscriptionBaseBundle bundle, final SubscriptionBase baseSubscription, final String productName, final BillingPeriod term, final String planSet, final DateTime requestedDate) throws SubscriptionBaseApiException {
+        return createSubscription(bundle, baseSubscription, productName, term, planSet, null, requestedDate);
+    }
+
+    public DefaultSubscriptionBase createSubscription(final boolean noEvents, final SubscriptionBaseBundle bundle, final String productName, final BillingPeriod term, final String planSet) throws SubscriptionBaseApiException {
+        return createSubscription(noEvents, bundle, null, productName, term, planSet, null, null);
     }
 
-    public DefaultSubscriptionBase createSubscriptionWithBundle(final SubscriptionBaseBundle bundle, final SubscriptionBase baseSubscription, final String productName, final BillingPeriod term, final String planSet, final DateTime requestedDate)
+    public DefaultSubscriptionBase createSubscription(final SubscriptionBaseBundle bundle, final SubscriptionBase baseSubscription, final String productName, final BillingPeriod term, final String planSet, final PhaseType phaseType, final DateTime requestedDate)
             throws SubscriptionBaseApiException {
+        return createSubscription(false, bundle, baseSubscription, productName, term, planSet, phaseType, requestedDate);
+    }
+
+    public DefaultSubscriptionBase createSubscription(final boolean noEvents, @Nullable final SubscriptionBaseBundle bundle, final SubscriptionBase baseSubscription, final String productName, final BillingPeriod term, final String planSet, final PhaseType phaseType, final DateTime requestedDate)
+    throws SubscriptionBaseApiException {
+        // Make sure the right account information is used
+        final InternalCallContext internalCallContext = bundle == null ? this.internalCallContext : internalCallContextFactory.createInternalCallContext(bundle.getAccountId(),
+                                                                                                                                                         ObjectType.ACCOUNT,
+                                                                                                                                                         this.internalCallContext.getUpdatedBy(),
+                                                                                                                                                         this.internalCallContext.getCallOrigin(),
+                                                                                                                                                         this.internalCallContext.getContextUserType(),
+                                                                                                                                                         this.internalCallContext.getUserToken(),
+                                                                                                                                                         this.internalCallContext.getTenantRecordId());
+
+        boolean bundleExists = false;
+        if (bundle != null) {
+            try {
+                bundleExists = (subscriptionApi.getBundleFromId(bundle.getId(), internalCallContext) != null);
+            } catch (final SubscriptionBaseApiException ignored) {
+            }
+        }
 
-        if (requestedDate == null || requestedDate.compareTo(clock.getUTCNow()) <= 0) {
+        if (!noEvents && (requestedDate == null || requestedDate.compareTo(clock.getUTCNow()) <= 0)) {
             testListener.pushExpectedEvent(NextEvent.CREATE);
         }
-        final DefaultSubscriptionBase subscription = (DefaultSubscriptionBase) subscriptionApi.createSubscription(bundle,
-                                                                                                                  baseSubscription,
-                                                                                                                  new PlanPhaseSpecifier(productName, term, planSet, null), null,
-                                                                                                                  requestedDate == null ? clock.getUTCNow() : requestedDate, false, internalCallContext);
+
+        final ImmutableList<EntitlementSpecifier> entitlementSpecifiers = ImmutableList.<EntitlementSpecifier>of(new EntitlementSpecifier() {
+            @Override
+            public PlanPhaseSpecifier getPlanPhaseSpecifier() {
+                return new PlanPhaseSpecifier(productName, term, planSet, phaseType);
+            }
+
+            @Override
+            public List<PlanPhasePriceOverride> getOverrides() {
+                return null;
+            }
+        });
+        final SubscriptionBaseWithAddOnsSpecifier subscriptionBaseWithAddOnsSpecifier = new SubscriptionBaseWithAddOnsSpecifier(bundle == null ||!bundleExists ? null : bundle.getId(),
+                                                                                                                                bundle == null ? null : bundle.getExternalKey(),
+                                                                                                                                entitlementSpecifiers,
+                                                                                                                                requestedDate,
+                                                                                                                                false);
+        final SubscriptionBaseWithAddOns subscriptionBaseWithAddOns = subscriptionApi.createBaseSubscriptionsWithAddOns(ImmutableList.<SubscriptionBaseWithAddOnsSpecifier>of(subscriptionBaseWithAddOnsSpecifier),
+                                                                                                                        false,
+                                                                                                                        internalCallContext).get(0);
+        final DefaultSubscriptionBase subscription = (DefaultSubscriptionBase) subscriptionBaseWithAddOns.getSubscriptionBaseList().get(0);
         assertNotNull(subscription);
 
         testListener.assertListenerStatus();
 
+        mockNonEntityDao.addTenantRecordIdMapping(subscription.getId(), internalCallContext);
+        mockNonEntityDao.addAccountRecordIdMapping(subscription.getId(), internalCallContext);
+
+        mockNonEntityDao.addTenantRecordIdMapping(subscription.getBundleId(), internalCallContext);
+        mockNonEntityDao.addAccountRecordIdMapping(subscription.getBundleId(), internalCallContext);
+
         return subscription;
     }
 
@@ -174,8 +253,8 @@ public class TestSubscriptionHelper {
     }
 
     public void assertDateWithin(final DateTime in, final DateTime lower, final DateTime upper) {
-        assertTrue(in.isEqual(lower) || in.isAfter(lower));
-        assertTrue(in.isEqual(upper) || in.isBefore(upper));
+        assertTrue(in.isEqual(lower) || in.isAfter(lower), "in=" + in + ", lower=" + lower);
+        assertTrue(in.isEqual(upper) || in.isBefore(upper), "in=" + in + ", upper=" + upper);
     }
 
     public Duration getDurationMonth(final int months) {
diff --git a/subscription/src/test/java/org/killbill/billing/subscription/api/user/TestUserApiCancel.java b/subscription/src/test/java/org/killbill/billing/subscription/api/user/TestUserApiCancel.java
index 86c6d90..ce7fc6e 100644
--- a/subscription/src/test/java/org/killbill/billing/subscription/api/user/TestUserApiCancel.java
+++ b/subscription/src/test/java/org/killbill/billing/subscription/api/user/TestUserApiCancel.java
@@ -402,12 +402,9 @@ public class TestUserApiCancel extends SubscriptionTestSuiteWithEmbeddedDB {
         final BillingPeriod term = BillingPeriod.MONTHLY;
         final String planSetName = PriceListSet.DEFAULT_PRICELIST_NAME;
 
-
         final DateTime futureCreationDate = init.plusDays(10);
 
-        DefaultSubscriptionBase subscription = (DefaultSubscriptionBase) subscriptionInternalApi.createSubscription(bundle,
-                                                                                                                    null,
-                                                                                                                    testUtil.getProductSpecifier(productName, planSetName, term, null), null, futureCreationDate, false, internalCallContext);
+        DefaultSubscriptionBase subscription = testUtil.createSubscription(bundle, productName, term, planSetName, futureCreationDate);
         assertListenerStatus();
         assertNotNull(subscription);
         assertEquals(subscription.getState(), EntitlementState.PENDING);
diff --git a/subscription/src/test/java/org/killbill/billing/subscription/api/user/TestUserApiChangePlan.java b/subscription/src/test/java/org/killbill/billing/subscription/api/user/TestUserApiChangePlan.java
index b0202d2..9b83b81 100644
--- a/subscription/src/test/java/org/killbill/billing/subscription/api/user/TestUserApiChangePlan.java
+++ b/subscription/src/test/java/org/killbill/billing/subscription/api/user/TestUserApiChangePlan.java
@@ -494,7 +494,7 @@ public class TestUserApiChangePlan extends SubscriptionTestSuiteWithEmbeddedDB {
 
         // First try with default api (no date -> IMM) => Call should fail because subscription is PENDING
         final DryRunArguments dryRunArguments1 = testUtil.createDryRunArguments(subscription.getId(), subscription.getBundleId(), spec, null, SubscriptionEventType.CHANGE, null);
-        final List<SubscriptionBase> result1 = subscriptionInternalApi.getSubscriptionsForBundle(bundle.getId(), dryRunArguments1, internalCallContext);
+        final List<SubscriptionBase> result1 = subscriptionInternalApi.getSubscriptionsForBundle(subscription.getBundleId(), dryRunArguments1, internalCallContext);
 
         // Check we are seeing the right PENDING transition (pistol-monthly), not the START but the CHANGE on the same date
         assertEquals(((DefaultSubscriptionBase) result1.get(0)).getCurrentOrPendingPlan().getName(), "pistol-monthly");
@@ -503,7 +503,7 @@ public class TestUserApiChangePlan extends SubscriptionTestSuiteWithEmbeddedDB {
         // Second try with date prior to startDate => Call should fail because subscription is PENDING
         try {
             final DryRunArguments dryRunArguments2 = testUtil.createDryRunArguments(subscription.getId(), subscription.getBundleId(), spec, new LocalDate(startDate.minusDays(1)), SubscriptionEventType.CHANGE, null);
-            subscriptionInternalApi.getSubscriptionsForBundle(bundle.getId(), dryRunArguments2, internalCallContext);
+            subscriptionInternalApi.getSubscriptionsForBundle(subscription.getBundleId(), dryRunArguments2, internalCallContext);
             fail("Change plan should have failed : subscription PENDING");
         } catch (final SubscriptionBaseApiException e) {
             assertEquals(e.getCode(), ErrorCode.SUB_CHANGE_NON_ACTIVE.getCode());
@@ -517,7 +517,7 @@ public class TestUserApiChangePlan extends SubscriptionTestSuiteWithEmbeddedDB {
 
         // Third try with date equals to startDate  Call should succeed, but no event because action in future
         final DryRunArguments dryRunArguments3 = testUtil.createDryRunArguments(subscription.getId(), subscription.getBundleId(), spec, internalCallContext.toLocalDate(startDate), SubscriptionEventType.CHANGE, null);
-        final List<SubscriptionBase> result2 = subscriptionInternalApi.getSubscriptionsForBundle(bundle.getId(), dryRunArguments3, internalCallContext);
+        final List<SubscriptionBase> result2 = subscriptionInternalApi.getSubscriptionsForBundle(subscription.getBundleId(), dryRunArguments3, internalCallContext);
         // Check we are seeing the right PENDING transition (pistol-monthly), not the START but the CHANGE on the same date
         assertEquals(((DefaultSubscriptionBase) result2.get(0)).getCurrentOrPendingPlan().getName(), "pistol-monthly");
 
diff --git a/subscription/src/test/java/org/killbill/billing/subscription/api/user/TestUserApiCreate.java b/subscription/src/test/java/org/killbill/billing/subscription/api/user/TestUserApiCreate.java
index 027b801..b01709c 100644
--- a/subscription/src/test/java/org/killbill/billing/subscription/api/user/TestUserApiCreate.java
+++ b/subscription/src/test/java/org/killbill/billing/subscription/api/user/TestUserApiCreate.java
@@ -61,10 +61,8 @@ public class TestUserApiCreate extends SubscriptionTestSuiteWithEmbeddedDB {
         final BillingPeriod term = BillingPeriod.MONTHLY;
         final String planSetName = PriceListSet.DEFAULT_PRICELIST_NAME;
 
-        testListener.pushExpectedEvents(NextEvent.CREATE, NextEvent.PHASE);
-        final DefaultSubscriptionBase subscription = (DefaultSubscriptionBase) subscriptionInternalApi.createSubscription(bundle,
-                                                                                                                          null,
-                                                                                                                          testUtil.getProductSpecifier(productName, planSetName, term, null), null, requestedDate, false, internalCallContext);
+        testListener.pushExpectedEvents(NextEvent.PHASE);
+        final DefaultSubscriptionBase subscription = testUtil.createSubscription(bundle, productName, term, planSetName, requestedDate);
         assertListenerStatus();
         assertNotNull(subscription);
 
@@ -92,12 +90,8 @@ public class TestUserApiCreate extends SubscriptionTestSuiteWithEmbeddedDB {
         assertNotNull(newBundle);
         assertEquals(newBundle.getOriginalCreatedDate().compareTo(bundle.getCreatedDate()), 0);
 
-
-        testListener.pushExpectedEvents(NextEvent.CREATE, NextEvent.PHASE);
-        final DefaultSubscriptionBase newSubscription = (DefaultSubscriptionBase) subscriptionInternalApi.createSubscription(newBundle,
-                                                                                                                             null,
-                                                                                                                             testUtil.getProductSpecifier(productName, planSetName, term, null), null, requestedDate, false, internalCallContext);
-
+        testListener.pushExpectedEvents(NextEvent.PHASE);
+        final DefaultSubscriptionBase newSubscription = testUtil.createSubscription(bundle, productName, term, planSetName, requestedDate);
         subscriptionInternalApi.updateExternalKey(newBundle.getId(), "myNewSuperKey", internalCallContext);
 
         final SubscriptionBaseBundle bundleWithNewKey = subscriptionInternalApi.getBundleFromId(newBundle.getId(), internalCallContext);
@@ -117,11 +111,7 @@ public class TestUserApiCreate extends SubscriptionTestSuiteWithEmbeddedDB {
         final String planSetName = PriceListSet.DEFAULT_PRICELIST_NAME;
 
         testListener.pushExpectedEvent(NextEvent.PHASE);
-        testListener.pushExpectedEvent(NextEvent.CREATE);
-
-        final DefaultSubscriptionBase subscription = (DefaultSubscriptionBase) subscriptionInternalApi.createSubscription(bundle,
-                                                                                                                          null,
-                                                                                                                          testUtil.getProductSpecifier(productName, planSetName, term, null), null, requestedDate, false, internalCallContext);
+        final DefaultSubscriptionBase subscription = testUtil.createSubscription(bundle, productName, term, planSetName, requestedDate);
         assertNotNull(subscription);
 
         //
@@ -134,7 +124,7 @@ public class TestUserApiCreate extends SubscriptionTestSuiteWithEmbeddedDB {
         final SubscriptionBaseEvent trialEvent = events.get(0);
         final SubscriptionBaseEvent phaseEvent = events.get(1);
 
-        assertEquals(subscription.getBundleId(), bundle.getId());
+        assertEquals(subscription.getBundleExternalKey(), bundle.getExternalKey());
         assertEquals(subscription.getStartDate(), requestedDate);
 
         assertListenerStatus();
@@ -156,14 +146,10 @@ public class TestUserApiCreate extends SubscriptionTestSuiteWithEmbeddedDB {
         final BillingPeriod term = BillingPeriod.MONTHLY;
         final String planSetName = PriceListSet.DEFAULT_PRICELIST_NAME;
 
-        testListener.pushExpectedEvent(NextEvent.CREATE);
-
-        final DefaultSubscriptionBase subscription = (DefaultSubscriptionBase) subscriptionInternalApi.createSubscription(bundle,
-                                                                                                                          null,
-                                                                                                                          testUtil.getProductSpecifier(productName, planSetName, term, PhaseType.EVERGREEN), null, clock.getUTCNow(), false, internalCallContext);
+        final DefaultSubscriptionBase subscription = testUtil.createSubscription(bundle, productName, term, planSetName, PhaseType.EVERGREEN, clock.getUTCNow());
         assertNotNull(subscription);
 
-        assertEquals(subscription.getBundleId(), bundle.getId());
+        assertEquals(subscription.getBundleExternalKey(), bundle.getExternalKey());
         testUtil.assertDateWithin(subscription.getStartDate(), init, clock.getUTCNow());
         testUtil.assertDateWithin(subscription.getBundleStartDate(), init, clock.getUTCNow());
 
@@ -188,15 +174,10 @@ public class TestUserApiCreate extends SubscriptionTestSuiteWithEmbeddedDB {
         final BillingPeriod term = BillingPeriod.MONTHLY;
         final String planSetName = PriceListSet.DEFAULT_PRICELIST_NAME;
 
-        testListener.pushExpectedEvent(NextEvent.CREATE);
-
-        final DefaultSubscriptionBase subscription = (DefaultSubscriptionBase) subscriptionInternalApi.createSubscription(bundle,
-                                                                                                                          null,
-                                                                                                                          testUtil.getProductSpecifier(productName, planSetName, term, null),
-                                                                                                                          null, clock.getUTCNow(), false, internalCallContext);
+        final DefaultSubscriptionBase subscription = testUtil.createSubscription(bundle, productName, term, planSetName, clock.getUTCNow());
         assertNotNull(subscription);
 
-        assertEquals(subscription.getBundleId(), bundle.getId());
+        assertEquals(subscription.getBundleExternalKey(), bundle.getExternalKey());
         testUtil.assertDateWithin(subscription.getStartDate(), init, clock.getUTCNow());
         testUtil.assertDateWithin(subscription.getBundleStartDate(), init, clock.getUTCNow());
 
@@ -236,13 +217,8 @@ public class TestUserApiCreate extends SubscriptionTestSuiteWithEmbeddedDB {
         final BillingPeriod term = BillingPeriod.ANNUAL;
         final String planSetName = "gunclubDiscount";
 
-        testListener.pushExpectedEvent(NextEvent.CREATE);
-
         // CREATE SUBSCRIPTION
-        DefaultSubscriptionBase subscription = (DefaultSubscriptionBase) subscriptionInternalApi.createSubscription(bundle,
-                                                                                                                    null,
-                                                                                                                    testUtil.getProductSpecifier(productName, planSetName, term, null),
-                                                                                                                    null, clock.getUTCNow(), false, internalCallContext);
+        DefaultSubscriptionBase subscription = testUtil.createSubscription(bundle, productName, term, planSetName, clock.getUTCNow());
         assertNotNull(subscription);
 
         PlanPhase currentPhase = subscription.getCurrentPhase();
@@ -279,12 +255,7 @@ public class TestUserApiCreate extends SubscriptionTestSuiteWithEmbeddedDB {
         final BillingPeriod term = BillingPeriod.ANNUAL;
         final String planSetName = PriceListSet.DEFAULT_PRICELIST_NAME;
 
-        testListener.pushExpectedEvent(NextEvent.CREATE);
-
-        final DefaultSubscriptionBase subscription = (DefaultSubscriptionBase) subscriptionInternalApi.createSubscription(bundle,
-                                                                                                                          null,
-                                                                                                                          testUtil.getProductSpecifier(productName, planSetName, term, null),
-                                                                                                                          null, clock.getUTCNow(), false, internalCallContext);
+        final DefaultSubscriptionBase subscription = testUtil.createSubscription(bundle, productName, term, planSetName, clock.getUTCNow());
         assertNotNull(subscription);
 
         assertListenerStatus();
@@ -301,9 +272,7 @@ public class TestUserApiCreate extends SubscriptionTestSuiteWithEmbeddedDB {
 
         final DateTime futureCreationDate = init.plusDays(10);
 
-        DefaultSubscriptionBase subscription = (DefaultSubscriptionBase) subscriptionInternalApi.createSubscription(bundle,
-                                                                                                                    null,
-                                                                                                                    testUtil.getProductSpecifier(productName, planSetName, term, null), null, futureCreationDate, false, internalCallContext);
+        DefaultSubscriptionBase subscription = testUtil.createSubscription(bundle, productName, term, planSetName, futureCreationDate);
         assertListenerStatus();
         assertNotNull(subscription);
         assertEquals(subscription.getState(), EntitlementState.PENDING);
diff --git a/subscription/src/test/java/org/killbill/billing/subscription/api/user/TestUserApiError.java b/subscription/src/test/java/org/killbill/billing/subscription/api/user/TestUserApiError.java
index 47b6164..27a34bd 100644
--- a/subscription/src/test/java/org/killbill/billing/subscription/api/user/TestUserApiError.java
+++ b/subscription/src/test/java/org/killbill/billing/subscription/api/user/TestUserApiError.java
@@ -58,8 +58,8 @@ public class TestUserApiError extends SubscriptionTestSuiteNoDB {
     }
 
     @Test(groups = "fast")
-    public void testCreateSubscriptionNoBundle() {
-        tCreateSubscriptionInternal(null, "Shotgun", BillingPeriod.ANNUAL, PriceListSet.DEFAULT_PRICELIST_NAME, ErrorCode.SUB_CREATE_NO_BUNDLE);
+    public void testCreateSubscriptionNoBundle() throws SubscriptionBaseApiException {
+        testUtil.createSubscription(null, "Shotgun", BillingPeriod.ANNUAL, PriceListSet.DEFAULT_PRICELIST_NAME);
     }
 
     @Test(groups = "fast")
@@ -75,39 +75,20 @@ public class TestUserApiError extends SubscriptionTestSuiteNoDB {
 
     @Test(groups = "fast")
     public void testCreateSubscriptionAddOnNotAvailable() throws SubscriptionBaseApiException {
-        final SubscriptionBaseBundle aoBundle = subscriptionInternalApi.createBundleForAccount(bundle.getAccountId(), "myAOBundle", true, internalCallContext);
-        mockNonEntityDao.addTenantRecordIdMapping(aoBundle.getId(), internalCallContext);
-        mockNonEntityDao.addAccountRecordIdMapping(aoBundle.getId(), internalCallContext);
-
-        testUtil.createSubscriptionWithBundle(aoBundle, null, "Pistol", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
-        tCreateSubscriptionInternal(aoBundle, "Telescopic-Scope", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, ErrorCode.SUB_CREATE_AO_NOT_AVAILABLE);
+        testUtil.createSubscription(bundle, "Pistol", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME);
+        tCreateSubscriptionInternal(bundle, "Telescopic-Scope", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, ErrorCode.SUB_CREATE_AO_NOT_AVAILABLE);
     }
 
     @Test(groups = "fast")
     public void testCreateSubscriptionAddOnIncluded() throws SubscriptionBaseApiException {
-        final SubscriptionBaseBundle aoBundle = subscriptionInternalApi.createBundleForAccount(bundle.getAccountId(), "myAOBundle", true, internalCallContext);
-        mockNonEntityDao.addTenantRecordIdMapping(aoBundle.getId(), internalCallContext);
-        mockNonEntityDao.addAccountRecordIdMapping(aoBundle.getId(), internalCallContext);
-
-        testUtil.createSubscriptionWithBundle(aoBundle, null, "Assault-Rifle", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
-        tCreateSubscriptionInternal(aoBundle, "Telescopic-Scope", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, ErrorCode.SUB_CREATE_AO_ALREADY_INCLUDED);
+        testUtil.createSubscription(bundle, "Assault-Rifle", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME);
+        tCreateSubscriptionInternal(bundle, "Telescopic-Scope", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, ErrorCode.SUB_CREATE_AO_ALREADY_INCLUDED);
     }
 
     private void tCreateSubscriptionInternal(@Nullable final SubscriptionBaseBundle bundle, @Nullable final String productName,
                                              @Nullable final BillingPeriod term, final String planSet, final ErrorCode expected) {
-        SubscriptionBase baseSubscription = null;
-        if (bundle != null) {
-            try {
-                baseSubscription = subscriptionInternalApi.getBaseSubscription(bundle.getId(), internalCallContext);
-            } catch (final SubscriptionBaseApiException ignored) {
-            }
-        }
-
         try {
-            subscriptionInternalApi.createSubscription(bundle,
-                                                       baseSubscription,
-                                                       testUtil.getProductSpecifier(productName, planSet, term, null),
-                                                       null, clock.getUTCNow(), false, internalCallContext);
+            testUtil.createSubscription(true, bundle, productName, term, planSet);
             Assert.fail("Exception expected, error code: " + expected);
         } catch (final SubscriptionBaseApiException e) {
             assertEquals(e.getCode(), expected.getCode());
diff --git a/subscription/src/test/java/org/killbill/billing/subscription/DefaultSubscriptionTestInitializer.java b/subscription/src/test/java/org/killbill/billing/subscription/DefaultSubscriptionTestInitializer.java
index b0a541b..8da169d 100644
--- a/subscription/src/test/java/org/killbill/billing/subscription/DefaultSubscriptionTestInitializer.java
+++ b/subscription/src/test/java/org/killbill/billing/subscription/DefaultSubscriptionTestInitializer.java
@@ -34,6 +34,7 @@ import org.killbill.billing.lifecycle.api.BusService;
 import org.killbill.billing.mock.MockAccountBuilder;
 import org.killbill.billing.subscription.api.SubscriptionBaseInternalApi;
 import org.killbill.billing.subscription.api.SubscriptionBaseService;
+import org.killbill.billing.subscription.api.user.DefaultSubscriptionBaseBundle;
 import org.killbill.billing.subscription.api.user.SubscriptionBaseBundle;
 import org.killbill.billing.subscription.engine.core.DefaultSubscriptionBaseService;
 import org.killbill.billing.util.UUIDs;
@@ -80,10 +81,9 @@ public class DefaultSubscriptionTestInitializer implements SubscriptionTestIniti
         return accountData;
     }
 
-    public SubscriptionBaseBundle initBundle(final UUID accountId, final SubscriptionBaseInternalApi subscriptionApi, final InternalCallContext callContext) throws Exception {
-        final SubscriptionBaseBundle bundle = subscriptionApi.createBundleForAccount(accountId, DEFAULT_BUNDLE_KEY, true, callContext);
-        assertNotNull(bundle);
-        return bundle;
+    public SubscriptionBaseBundle initBundle(final UUID accountId, final SubscriptionBaseInternalApi subscriptionApi, final Clock clock, final InternalCallContext callContext) throws Exception {
+        final DateTime utcNow = clock.getUTCNow();
+        return new DefaultSubscriptionBaseBundle(DEFAULT_BUNDLE_KEY, accountId, utcNow, utcNow, utcNow, utcNow);
     }
 
     public void startTestFramework(final TestApiListener testListener,
diff --git a/subscription/src/test/java/org/killbill/billing/subscription/engine/dao/MockSubscriptionDaoMemory.java b/subscription/src/test/java/org/killbill/billing/subscription/engine/dao/MockSubscriptionDaoMemory.java
index d0afd23..ed8e8c2 100644
--- a/subscription/src/test/java/org/killbill/billing/subscription/engine/dao/MockSubscriptionDaoMemory.java
+++ b/subscription/src/test/java/org/killbill/billing/subscription/engine/dao/MockSubscriptionDaoMemory.java
@@ -70,6 +70,7 @@ import org.killbill.notificationq.api.NotificationQueueService.NoSuchNotificatio
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import com.google.common.base.MoreObjects;
 import com.google.inject.Inject;
 
 public class MockSubscriptionDaoMemory extends MockEntityDaoBase<SubscriptionBundleModelDao, SubscriptionBaseBundle, SubscriptionApiException> implements SubscriptionDao {
@@ -175,7 +176,13 @@ public class MockSubscriptionDaoMemory extends MockEntityDaoBase<SubscriptionBun
 
     @Override
     public SubscriptionBaseBundle createSubscriptionBundle(final DefaultSubscriptionBaseBundle bundle, final Catalog catalog, final boolean renameCancelledBundleIfExist, final InternalCallContext context) {
-        bundles.add(bundle);
+        bundles.add(new DefaultSubscriptionBaseBundle(bundle.getId(),
+                                                      MoreObjects.firstNonNull(bundle.getExternalKey(), UUID.randomUUID().toString()),
+                                                      bundle.getAccountId(),
+                                                      bundle.getLastSysUpdateDate(),
+                                                      bundle.getOriginalCreatedDate(),
+                                                      bundle.getCreatedDate(),
+                                                      bundle.getUpdatedDate()));
         mockNonEntityDao.addTenantRecordIdMapping(bundle.getId(), context);
         return getSubscriptionBundleFromId(bundle.getId(), context);
     }
@@ -195,40 +202,12 @@ public class MockSubscriptionDaoMemory extends MockEntityDaoBase<SubscriptionBun
         throw new UnsupportedOperationException();
     }
 
-    /*
     @Override
-    public List<SubscriptionBase> getSubscriptionsForAccountAndKey(final UUID accountId, final String bundleKey, final InternalTenantContext callcontext) {
-
-        for (final SubscriptionBaseBundle cur : bundles) {
-            if (cur.getExternalKey().equals(bundleKey) && cur.getAccountId().equals(bundleKey)) {
-                return getSubscriptions(cur.getId(), callcontext);
-            }
-        }
-        return Collections.emptyList();
-    }
-    */
-
-    @Override
-    public List<SubscriptionBaseEvent> createSubscription(final DefaultSubscriptionBase subscription, final List<SubscriptionBaseEvent> initialEvents,
-                                                          final Catalog catalog, final InternalCallContext context) {
-        synchronized (events) {
-            events.addAll(initialEvents);
-            for (final SubscriptionBaseEvent cur : initialEvents) {
-                recordFutureNotificationFromTransaction(null, cur.getEffectiveDate(), new SubscriptionNotificationKey(cur.getId()), context);
-            }
-        }
-        final SubscriptionBase updatedSubscription = buildSubscription(subscription, context);
-        subscriptions.add(updatedSubscription);
-        mockNonEntityDao.addTenantRecordIdMapping(updatedSubscription.getId(), context);
-
-        return initialEvents;
-    }
-
-    @Override
-    public void createSubscriptionsWithAddOns(final List<SubscriptionBaseWithAddOns> subscriptions,
-                                              final Map<UUID, List<SubscriptionBaseEvent>> initialEventsMap,
-                                              final Catalog catalog,
-                                              final InternalCallContext context) {
+    public List<SubscriptionBaseEvent> createSubscriptionsWithAddOns(final List<SubscriptionBaseWithAddOns> subscriptions,
+                                                                     final Map<UUID, List<SubscriptionBaseEvent>> initialEventsMap,
+                                                                     final Catalog catalog,
+                                                                     final InternalCallContext context) {
+        final List<SubscriptionBaseEvent> createdEvents = new LinkedList<SubscriptionBaseEvent>();
         synchronized (events) {
             for (final SubscriptionBaseWithAddOns subscription : subscriptions) {
                 for (final SubscriptionBase subscriptionBase : subscription.getSubscriptionBaseList()) {
@@ -240,9 +219,13 @@ public class MockSubscriptionDaoMemory extends MockEntityDaoBase<SubscriptionBun
                     final SubscriptionBase updatedSubscription = buildSubscription((DefaultSubscriptionBase) subscriptionBase, context);
                     this.subscriptions.add(updatedSubscription);
                     mockNonEntityDao.addTenantRecordIdMapping(updatedSubscription.getId(), context);
+
+                    createdEvents.addAll(initialEvents);
                 }
             }
         }
+
+        return createdEvents;
     }
 
     @Override
diff --git a/subscription/src/test/java/org/killbill/billing/subscription/engine/dao/TestSubscriptionDao.java b/subscription/src/test/java/org/killbill/billing/subscription/engine/dao/TestSubscriptionDao.java
index 2fc6531..ce7e0ca 100644
--- a/subscription/src/test/java/org/killbill/billing/subscription/engine/dao/TestSubscriptionDao.java
+++ b/subscription/src/test/java/org/killbill/billing/subscription/engine/dao/TestSubscriptionDao.java
@@ -1,6 +1,6 @@
 /*
- * Copyright 2014-2017 Groupon, Inc
- * Copyright 2014-2017 The Billing Project, LLC
+ * Copyright 2014-2018 Groupon, Inc
+ * Copyright 2014-2018 The Billing Project, LLC
  *
  * The Billing Project licenses this file to you under the Apache License, version 2.0
  * (the "License"); you may not use this file except in compliance with the
@@ -28,8 +28,11 @@ import org.killbill.billing.api.TestApiListener.NextEvent;
 import org.killbill.billing.catalog.DefaultPriceListSet;
 import org.killbill.billing.catalog.api.ProductCategory;
 import org.killbill.billing.subscription.SubscriptionTestSuiteWithEmbeddedDB;
+import org.killbill.billing.subscription.api.SubscriptionBase;
+import org.killbill.billing.subscription.api.SubscriptionBaseWithAddOns;
 import org.killbill.billing.subscription.api.user.DefaultSubscriptionBase;
 import org.killbill.billing.subscription.api.user.DefaultSubscriptionBaseBundle;
+import org.killbill.billing.subscription.api.user.DefaultSubscriptionBaseWithAddOns;
 import org.killbill.billing.subscription.api.user.SubscriptionBaseApiException;
 import org.killbill.billing.subscription.api.user.SubscriptionBaseBundle;
 import org.killbill.billing.subscription.api.user.SubscriptionBuilder;
@@ -46,6 +49,7 @@ import org.testng.annotations.BeforeMethod;
 import org.testng.annotations.Test;
 
 import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
 
 import static org.testng.Assert.assertEquals;
 
@@ -111,7 +115,13 @@ public class TestSubscriptionDao extends SubscriptionTestSuiteWithEmbeddedDB {
 
         final DefaultSubscriptionBase subscription = new DefaultSubscriptionBase(builder);
         testListener.pushExpectedEvents(NextEvent.CREATE);
-        dao.createSubscription(subscription, ImmutableList.of(creationEvent), catalog, internalCallContext);
+        final SubscriptionBaseWithAddOns subscriptionBaseWithAddOns = new DefaultSubscriptionBaseWithAddOns(bundle,
+                                                                                                            ImmutableList.<SubscriptionBase>of(subscription),
+                                                                                                            clock.getUTCNow());
+        dao.createSubscriptionsWithAddOns(ImmutableList.<SubscriptionBaseWithAddOns>of(subscriptionBaseWithAddOns),
+                                          ImmutableMap.<UUID, List<SubscriptionBaseEvent>>of(subscription.getId(), ImmutableList.<SubscriptionBaseEvent>of(creationEvent)),
+                                          catalog,
+                                          internalCallContext);
         assertListenerStatus();
 
         // Operation Should now fail
diff --git a/subscription/src/test/java/org/killbill/billing/subscription/SubscriptionTestInitializer.java b/subscription/src/test/java/org/killbill/billing/subscription/SubscriptionTestInitializer.java
index 68a9921..dea2503 100644
--- a/subscription/src/test/java/org/killbill/billing/subscription/SubscriptionTestInitializer.java
+++ b/subscription/src/test/java/org/killbill/billing/subscription/SubscriptionTestInitializer.java
@@ -39,7 +39,7 @@ public interface SubscriptionTestInitializer {
 
     public AccountData initAccountData(Clock clock);
 
-    public SubscriptionBaseBundle initBundle(final UUID accountId, final SubscriptionBaseInternalApi subscriptionApi, final InternalCallContext callContext) throws Exception;
+    public SubscriptionBaseBundle initBundle(final UUID accountId, final SubscriptionBaseInternalApi subscriptionApi, final Clock clock, final InternalCallContext callContext) throws Exception;
 
     public void startTestFramework(final TestApiListener testListener,
                                    final ClockMock clock,
diff --git a/subscription/src/test/java/org/killbill/billing/subscription/SubscriptionTestSuiteNoDB.java b/subscription/src/test/java/org/killbill/billing/subscription/SubscriptionTestSuiteNoDB.java
index 8563da4..5daabf1 100644
--- a/subscription/src/test/java/org/killbill/billing/subscription/SubscriptionTestSuiteNoDB.java
+++ b/subscription/src/test/java/org/killbill/billing/subscription/SubscriptionTestSuiteNoDB.java
@@ -23,7 +23,10 @@ import java.util.UUID;
 import javax.inject.Inject;
 
 import org.killbill.billing.GuicyKillbillTestSuiteNoDB;
+import org.killbill.billing.account.api.Account;
 import org.killbill.billing.account.api.AccountData;
+import org.killbill.billing.account.api.AccountInternalApi;
+import org.killbill.billing.account.api.AccountUserApi;
 import org.killbill.billing.account.api.ImmutableAccountData;
 import org.killbill.billing.account.api.ImmutableAccountInternalApi;
 import org.killbill.billing.api.TestApiListener;
@@ -31,7 +34,6 @@ import org.killbill.billing.callcontext.InternalTenantContext;
 import org.killbill.billing.catalog.api.Catalog;
 import org.killbill.billing.catalog.api.CatalogInternalApi;
 import org.killbill.billing.catalog.api.CatalogService;
-import org.killbill.billing.catalog.api.CatalogUserApi;
 import org.killbill.billing.dao.MockNonEntityDao;
 import org.killbill.billing.lifecycle.api.BusService;
 import org.killbill.billing.platform.api.KillbillConfigSource;
@@ -60,13 +62,16 @@ import org.testng.annotations.BeforeMethod;
 import com.google.inject.Guice;
 import com.google.inject.Injector;
 import com.google.inject.Stage;
-import org.killbill.billing.util.UUIDs;
 
 public class SubscriptionTestSuiteNoDB extends GuicyKillbillTestSuiteNoDB {
 
     protected static final Logger log = LoggerFactory.getLogger(SubscriptionTestSuiteNoDB.class);
 
     @Inject
+    protected AccountUserApi accountUserApi;
+    @Inject
+    protected AccountInternalApi accountInternalApi;
+    @Inject
     protected ImmutableAccountInternalApi immutableAccountInternalApi;
     @Inject
     protected SubscriptionBaseService subscriptionBaseService;
@@ -131,7 +136,6 @@ public class SubscriptionTestSuiteNoDB extends GuicyKillbillTestSuiteNoDB {
 
     @BeforeMethod(groups = "fast")
     public void beforeMethod() throws Exception {
-
         // CLEANUP ALL DB TABLES OR IN MEMORY STRUCTURES
         ((MockSubscriptionDaoMemory) dao).reset();
 
@@ -139,13 +143,25 @@ public class SubscriptionTestSuiteNoDB extends GuicyKillbillTestSuiteNoDB {
 
         this.catalog = subscriptionTestInitializer.initCatalog(catalogService, internalCallContext);
         this.accountData = subscriptionTestInitializer.initAccountData(clock);
-        final UUID accountId = UUIDs.randomUUID();
+
+        final Account account = GuicyKillbillTestSuiteNoDB.createMockAccount(accountData,
+                                                                             accountUserApi,
+                                                                             accountInternalApi,
+                                                                             immutableAccountInternalApi,
+                                                                             mockNonEntityDao,
+                                                                             clock,
+                                                                             internalCallContextFactory,
+                                                                             callContext,
+                                                                             internalCallContext);
+        final UUID accountId = account.getId();
+        mockNonEntityDao.addAccountRecordIdMapping(accountId, internalCallContext);
         mockNonEntityDao.addTenantRecordIdMapping(accountId, internalCallContext);
+        mockNonEntityDao.addAccountIdMapping(internalCallContext.getAccountRecordId(), accountId);
 
         final ImmutableAccountData immutableAccountData = Mockito.mock(ImmutableAccountData.class);
         Mockito.when(immutableAccountInternalApi.getImmutableAccountDataByRecordId(Mockito.<Long>eq(internalCallContext.getAccountRecordId()), Mockito.<InternalTenantContext>any())).thenReturn(immutableAccountData);
 
-        this.bundle = subscriptionTestInitializer.initBundle(accountId, subscriptionInternalApi, internalCallContext);
+        this.bundle = subscriptionTestInitializer.initBundle(accountId, subscriptionInternalApi, clock, internalCallContext);
         mockNonEntityDao.addTenantRecordIdMapping(bundle.getId(), internalCallContext);
         mockNonEntityDao.addAccountRecordIdMapping(bundle.getId(), internalCallContext);
     }
diff --git a/subscription/src/test/java/org/killbill/billing/subscription/SubscriptionTestSuiteWithEmbeddedDB.java b/subscription/src/test/java/org/killbill/billing/subscription/SubscriptionTestSuiteWithEmbeddedDB.java
index 2b5da99..94d76ad 100644
--- a/subscription/src/test/java/org/killbill/billing/subscription/SubscriptionTestSuiteWithEmbeddedDB.java
+++ b/subscription/src/test/java/org/killbill/billing/subscription/SubscriptionTestSuiteWithEmbeddedDB.java
@@ -1,7 +1,7 @@
 /*
  * Copyright 2010-2013 Ning, Inc.
- * Copyright 2014-2015 Groupon, Inc
- * Copyright 2014-2015 The Billing Project, LLC
+ * Copyright 2014-2018 Groupon, Inc
+ * Copyright 2014-2018 The Billing Project, LLC
  *
  * The Billing Project licenses this file to you under the Apache License, version 2.0
  * (the "License"); you may not use this file except in compliance with the
@@ -120,7 +120,7 @@ public class SubscriptionTestSuiteWithEmbeddedDB extends GuicyKillbillTestSuiteW
         this.catalog = subscriptionTestInitializer.initCatalog(catalogService, internalCallContext);
         this.accountData = subscriptionTestInitializer.initAccountData(clock);
         final Account account = createAccount(accountData);
-        this.bundle = subscriptionTestInitializer.initBundle(account.getId(), subscriptionInternalApi, internalCallContext);
+        this.bundle = subscriptionTestInitializer.initBundle(account.getId(), subscriptionInternalApi, clock, internalCallContext);
     }
 
     @AfterMethod(groups = "slow")
diff --git a/util/src/test/java/org/killbill/billing/dao/MockNonEntityDao.java b/util/src/test/java/org/killbill/billing/dao/MockNonEntityDao.java
index 0a1812e..81159a0 100644
--- a/util/src/test/java/org/killbill/billing/dao/MockNonEntityDao.java
+++ b/util/src/test/java/org/killbill/billing/dao/MockNonEntityDao.java
@@ -1,7 +1,7 @@
 /*
  * Copyright 2010-2013 Ning, Inc.
- * Copyright 2014-2015 Groupon, Inc
- * Copyright 2014-2015 The Billing Project, LLC
+ * Copyright 2014-2018 Groupon, Inc
+ * Copyright 2014-2018 The Billing Project, LLC
  *
  * The Billing Project licenses this file to you under the Apache License, version 2.0
  * (the "License"); you may not use this file except in compliance with the
@@ -32,10 +32,13 @@ import org.killbill.billing.util.dao.NonEntitySqlDao;
 import org.killbill.billing.util.dao.TableName;
 import org.skife.jdbi.v2.Handle;
 
+import static org.killbill.billing.ObjectType.ACCOUNT;
+
 public class MockNonEntityDao implements NonEntityDao {
 
     private final Map<UUID, Long> tenantRecordIdMappings = new HashMap<UUID, Long>();
     private final Map<UUID, Long> accountRecordIdMappings = new HashMap<UUID, Long>();
+    private final Map<Long, UUID> accountIdMappings = new HashMap<Long, UUID>();
 
     public void addTenantRecordIdMapping(final UUID objectId, final InternalTenantContext context) {
         tenantRecordIdMappings.put(objectId, context.getTenantRecordId());
@@ -45,6 +48,10 @@ public class MockNonEntityDao implements NonEntityDao {
         accountRecordIdMappings.put(objectId, context.getAccountRecordId());
     }
 
+    public void addAccountIdMapping(final Long objectRecordId, final UUID objectId) {
+        accountIdMappings.put(objectRecordId, objectId);
+    }
+
     @Override
     public Long retrieveRecordIdFromObject(final UUID objectId, final ObjectType objectType, @Nullable final CacheController<String, Long> cache) {
         return null;
@@ -77,7 +84,11 @@ public class MockNonEntityDao implements NonEntityDao {
 
     @Override
     public UUID retrieveIdFromObject(final Long recordId, final ObjectType objectType, @Nullable final CacheController<String, UUID> cache) {
-        return null;
+        if (objectType == ACCOUNT) {
+            return accountIdMappings.get(recordId);
+        } else {
+            return null;
+        }
     }
 
     @Override
diff --git a/util/src/test/java/org/killbill/billing/GuicyKillbillTestSuiteNoDB.java b/util/src/test/java/org/killbill/billing/GuicyKillbillTestSuiteNoDB.java
index cc2e479..a337c8f 100644
--- a/util/src/test/java/org/killbill/billing/GuicyKillbillTestSuiteNoDB.java
+++ b/util/src/test/java/org/killbill/billing/GuicyKillbillTestSuiteNoDB.java
@@ -1,7 +1,9 @@
 /*
  * Copyright 2010-2013 Ning, Inc.
+ * Copyright 2014-2018 Groupon, Inc
+ * Copyright 2014-2018 The Billing Project, LLC
  *
- * Ning licenses this file to you under the Apache License, version 2.0
+ * The Billing Project licenses this file to you under the Apache License, version 2.0
  * (the "License"); you may not use this file except in compliance with the
  * License.  You may obtain a copy of the License at:
  *
@@ -20,6 +22,7 @@ import java.util.UUID;
 
 import org.killbill.billing.account.api.Account;
 import org.killbill.billing.account.api.AccountApiException;
+import org.killbill.billing.account.api.AccountData;
 import org.killbill.billing.account.api.AccountInternalApi;
 import org.killbill.billing.account.api.AccountUserApi;
 import org.killbill.billing.account.api.ImmutableAccountInternalApi;
@@ -34,7 +37,7 @@ import org.mockito.Mockito;
 
 public class GuicyKillbillTestSuiteNoDB extends GuicyKillbillTestSuite {
 
-    public static Account createMockAccount(final Account accountData,
+    public static Account createMockAccount(final AccountData accountData,
                                             final AccountUserApi accountUserApi,
                                             final AccountInternalApi accountInternalApi,
                                             final ImmutableAccountInternalApi immutableAccountInternalApi,