killbill-aplcache

Changes

Details

diff --git a/api/src/main/java/com/ning/billing/entitlement/api/SubscriptionTransitionType.java b/api/src/main/java/com/ning/billing/entitlement/api/SubscriptionTransitionType.java
index 8e75675..a788ee0 100644
--- a/api/src/main/java/com/ning/billing/entitlement/api/SubscriptionTransitionType.java
+++ b/api/src/main/java/com/ning/billing/entitlement/api/SubscriptionTransitionType.java
@@ -1,4 +1,4 @@
-/* 
+/*
  * Copyright 2010-2011 Ning, Inc.
  *
  * Ning licenses this file to you under the Apache License, version 2.0
@@ -19,6 +19,7 @@ public enum SubscriptionTransitionType {
     MIGRATE_ENTITLEMENT,
     CREATE,
     MIGRATE_BILLING,
+    TRANSFER,
     CHANGE,
     RE_CREATE,
     CANCEL,
diff --git a/api/src/main/java/com/ning/billing/entitlement/api/timeline/EntitlementTimelineApi.java b/api/src/main/java/com/ning/billing/entitlement/api/timeline/EntitlementTimelineApi.java
index 0bdf47f..18296be 100644
--- a/api/src/main/java/com/ning/billing/entitlement/api/timeline/EntitlementTimelineApi.java
+++ b/api/src/main/java/com/ning/billing/entitlement/api/timeline/EntitlementTimelineApi.java
@@ -1,4 +1,4 @@
-/* 
+/*
  * Copyright 2010-2011 Ning, Inc.
  *
  * Ning licenses this file to you under the Apache License, version 2.0
@@ -17,11 +17,20 @@ package com.ning.billing.entitlement.api.timeline;
 
 import java.util.UUID;
 
+import com.ning.billing.entitlement.api.user.SubscriptionBundle;
 import com.ning.billing.util.callcontext.CallContext;
 
 public interface EntitlementTimelineApi {
 
-    public BundleTimeline getBundleRepair(final UUID bundleId) throws EntitlementRepairException;
+    public BundleTimeline getBundleTimeline(final SubscriptionBundle bundle)
+    throws EntitlementRepairException;
 
-    public BundleTimeline repairBundle(final BundleTimeline input, final boolean dryRun, final CallContext context) throws EntitlementRepairException;
+    public BundleTimeline getBundleTimeline(final UUID accountId, final String bundleName)
+    throws EntitlementRepairException;
+
+    public BundleTimeline getBundleTimeline(final UUID bundleId)
+    throws EntitlementRepairException;
+
+    public BundleTimeline repairBundle(final BundleTimeline input, final boolean dryRun, final CallContext context)
+    throws EntitlementRepairException;
 }
diff --git a/api/src/main/java/com/ning/billing/entitlement/api/timeline/SubscriptionTimeline.java b/api/src/main/java/com/ning/billing/entitlement/api/timeline/SubscriptionTimeline.java
index 9374c62..81ed298 100644
--- a/api/src/main/java/com/ning/billing/entitlement/api/timeline/SubscriptionTimeline.java
+++ b/api/src/main/java/com/ning/billing/entitlement/api/timeline/SubscriptionTimeline.java
@@ -1,4 +1,4 @@
-/* 
+/*
  * Copyright 2010-2011 Ning, Inc.
  *
  * Ning licenses this file to you under the Apache License, version 2.0
@@ -33,6 +33,8 @@ public interface SubscriptionTimeline {
 
     public List<ExistingEvent> getExistingEvents();
 
+    public long getActiveVersion();
+
     public interface DeletedEvent {
         public UUID getEventId();
     }
@@ -48,5 +50,7 @@ public interface SubscriptionTimeline {
 
     public interface ExistingEvent extends DeletedEvent, NewEvent {
         public DateTime getEffectiveDate();
+
+        public String getPlanPhaseName();
     }
 }
diff --git a/api/src/main/java/com/ning/billing/entitlement/api/transfer/EntitlementTransferApi.java b/api/src/main/java/com/ning/billing/entitlement/api/transfer/EntitlementTransferApi.java
new file mode 100644
index 0000000..ce09e8c
--- /dev/null
+++ b/api/src/main/java/com/ning/billing/entitlement/api/transfer/EntitlementTransferApi.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2010-2011 Ning, Inc.
+ *
+ * Ning 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 com.ning.billing.entitlement.api.transfer;
+
+import java.util.UUID;
+
+import org.joda.time.DateTime;
+
+import com.ning.billing.util.callcontext.CallContext;
+
+public interface EntitlementTransferApi {
+
+    public void transferBundle(final UUID sourceAccountId, final UUID destAccountId, final String bundleKey, final DateTime requestedDate, final boolean transferAddOn, final CallContext context)
+        throws EntitlementTransferApiException;
+
+}
diff --git a/api/src/main/java/com/ning/billing/entitlement/api/transfer/EntitlementTransferApiException.java b/api/src/main/java/com/ning/billing/entitlement/api/transfer/EntitlementTransferApiException.java
new file mode 100644
index 0000000..150d772
--- /dev/null
+++ b/api/src/main/java/com/ning/billing/entitlement/api/transfer/EntitlementTransferApiException.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2010-2011 Ning, Inc.
+ *
+ * Ning 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 com.ning.billing.entitlement.api.transfer;
+
+import com.ning.billing.BillingExceptionBase;
+import com.ning.billing.ErrorCode;
+import com.ning.billing.catalog.api.CatalogApiException;
+import com.ning.billing.entitlement.api.timeline.EntitlementRepairException;
+
+public class EntitlementTransferApiException extends BillingExceptionBase {
+
+    private static final long serialVersionUID = 17086131L;
+
+    public EntitlementTransferApiException(final CatalogApiException e) {
+        super(e, e.getCode(), e.getMessage());
+    }
+
+    public EntitlementTransferApiException(final EntitlementRepairException e) {
+        super(e, e.getCode(), e.getMessage());
+    }
+    public EntitlementTransferApiException(final Throwable e, final ErrorCode code, final Object... args) {
+        super(e, code, args);
+    }
+
+    public EntitlementTransferApiException(final Throwable e, final int code, final String message) {
+        super(e, code, message);
+    }
+
+    public EntitlementTransferApiException(final ErrorCode code, final Object... args) {
+        super(code, args);
+    }
+}
diff --git a/api/src/main/java/com/ning/billing/entitlement/api/user/EntitlementUserApi.java b/api/src/main/java/com/ning/billing/entitlement/api/user/EntitlementUserApi.java
index 0ea8cea..2a78f6c 100644
--- a/api/src/main/java/com/ning/billing/entitlement/api/user/EntitlementUserApi.java
+++ b/api/src/main/java/com/ning/billing/entitlement/api/user/EntitlementUserApi.java
@@ -29,28 +29,30 @@ import com.ning.billing.util.callcontext.CallContext;
 
 public interface EntitlementUserApi {
 
-    public SubscriptionBundle getBundleFromId(UUID id) throws EntitlementUserApiException;
+    public SubscriptionBundle getBundleFromId(final UUID id) throws EntitlementUserApiException;
 
-    public Subscription getSubscriptionFromId(UUID id) throws EntitlementUserApiException;
+    public Subscription getSubscriptionFromId(final UUID id) throws EntitlementUserApiException;
 
-    public SubscriptionBundle getBundleForKey(String bundleKey) throws EntitlementUserApiException;
+    public List<SubscriptionBundle> getBundlesForKey(final String bundleKey) throws EntitlementUserApiException;
 
-    public List<SubscriptionBundle> getBundlesForAccount(UUID accountId);
+    public SubscriptionBundle getBundleForAccountAndKey(final UUID accountId, final String bundleKey) throws EntitlementUserApiException;
 
-    public List<Subscription> getSubscriptionsForBundle(UUID bundleId);
+    public List<SubscriptionBundle> getBundlesForAccount(final UUID accountId);
 
-    public List<Subscription> getSubscriptionsForKey(String bundleKey);
+    public List<Subscription> getSubscriptionsForBundle(final UUID bundleId);
 
-    public Subscription getBaseSubscription(UUID bundleId) throws EntitlementUserApiException;
+    public List<Subscription> getSubscriptionsForAccountAndKey(final UUID accountId, final String bundleKey);
 
-    public SubscriptionBundle createBundleForAccount(UUID accountId, String bundleKey, CallContext context)
+    public Subscription getBaseSubscription(final UUID bundleId) throws EntitlementUserApiException;
+
+    public SubscriptionBundle createBundleForAccount(final UUID accountId, final String bundleKey, final CallContext context)
             throws EntitlementUserApiException;
 
-    public Subscription createSubscription(UUID bundleId, PlanPhaseSpecifier spec, DateTime requestedDate, CallContext context)
+    public Subscription createSubscription(final UUID bundleId, final PlanPhaseSpecifier spec, final DateTime requestedDate, final CallContext context)
             throws EntitlementUserApiException;
 
-    public List<SubscriptionStatusDryRun> getDryRunChangePlanStatus(UUID subscriptionId, @Nullable String productName, DateTime requestedDate)
+    public List<SubscriptionStatusDryRun> getDryRunChangePlanStatus(final UUID subscriptionId, @Nullable final String productName, final DateTime requestedDate)
             throws EntitlementUserApiException;
 
-    public DateTime getNextBillingDate(UUID account);
+    public DateTime getNextBillingDate(final UUID account);
 }
diff --git a/api/src/main/java/com/ning/billing/entitlement/api/user/Subscription.java b/api/src/main/java/com/ning/billing/entitlement/api/user/Subscription.java
index 1e61a99..4eaadff 100644
--- a/api/src/main/java/com/ning/billing/entitlement/api/user/Subscription.java
+++ b/api/src/main/java/com/ning/billing/entitlement/api/user/Subscription.java
@@ -34,6 +34,17 @@ import com.ning.billing.util.entity.Entity;
 
 public interface Subscription extends Entity, Blockable {
 
+    public enum SubscriptionState {
+        ACTIVE,
+        CANCELLED
+    }
+
+    public enum SubscriptionSourceType {
+        NATIVE,
+        MIGRATED,
+        TRANSFERED
+    }
+
     public boolean cancel(DateTime requestedDate, boolean eot, CallContext context)
             throws EntitlementUserApiException;
 
@@ -50,15 +61,12 @@ public interface Subscription extends Entity, Blockable {
     public boolean recreate(PlanPhaseSpecifier spec, DateTime requestedDate, CallContext context)
             throws EntitlementUserApiException;
 
-    public enum SubscriptionState {
-        ACTIVE,
-        CANCELLED
-    }
-
     public UUID getBundleId();
 
     public SubscriptionState getState();
 
+    public SubscriptionSourceType getSourceType();
+
     public DateTime getStartDate();
 
     public DateTime getEndDate();
@@ -84,4 +92,5 @@ public interface Subscription extends Entity, Blockable {
     public List<EffectiveSubscriptionEvent> getBillingTransitions();
 
     public List<EffectiveSubscriptionEvent> getAllTransitions();
+
 }
diff --git a/api/src/main/java/com/ning/billing/glue/EntitlementModule.java b/api/src/main/java/com/ning/billing/glue/EntitlementModule.java
index 421e68d..88d467c 100644
--- a/api/src/main/java/com/ning/billing/glue/EntitlementModule.java
+++ b/api/src/main/java/com/ning/billing/glue/EntitlementModule.java
@@ -22,6 +22,8 @@ public interface EntitlementModule {
 
     public abstract void installEntitlementUserApi();
 
+    public abstract void installEntitlementTransferApi();
+
     public abstract void installEntitlementMigrationApi();
 
     public abstract void installChargeThruApi();
diff --git a/beatrix/src/test/java/com/ning/billing/beatrix/integration/TestRepairIntegration.java b/beatrix/src/test/java/com/ning/billing/beatrix/integration/TestRepairIntegration.java
index ae3ecc3..b349782 100644
--- a/beatrix/src/test/java/com/ning/billing/beatrix/integration/TestRepairIntegration.java
+++ b/beatrix/src/test/java/com/ning/billing/beatrix/integration/TestRepairIntegration.java
@@ -1,4 +1,4 @@
-/* 
+/*
  * Copyright 2010-2011 Ning, Inc.
  *
  * Ning licenses this file to you under the Apache License, version 2.0
@@ -105,7 +105,7 @@ public class TestRepairIntegration extends TestIntegrationBase {
         assertTrue(busHandler.isCompleted(DELAY));
 
 
-        // MOVE CLOCK A LITTLE BIT MORE -- EITHER STAY IN TRIAL OR GET OUT   
+        // MOVE CLOCK A LITTLE BIT MORE -- EITHER STAY IN TRIAL OR GET OUT
         final int duration = inTrial ? 3 : 35;
         it = new Interval(clock.getUTCNow(), clock.getUTCNow().plusDays(duration));
         if (!inTrial) {
@@ -121,7 +121,7 @@ public class TestRepairIntegration extends TestIntegrationBase {
         }
         final boolean ifRepair = false;
         if (ifRepair) {
-            BundleTimeline bundleRepair = repairApi.getBundleRepair(bundle.getId());
+            BundleTimeline bundleRepair = repairApi.getBundleTimeline(bundle.getId());
             sortEventsOnBundle(bundleRepair);
 
             // Quick check
@@ -194,6 +194,11 @@ public class TestRepairIntegration extends TestIntegrationBase {
             public List<DeletedEvent> getDeletedEvents() {
                 return deletedEvents;
             }
+
+            @Override
+            public long getActiveVersion() {
+                return 0;
+            }
         };
     }
 
@@ -252,6 +257,11 @@ public class TestRepairIntegration extends TestIntegrationBase {
             public DateTime getEffectiveDate() {
                 return effectiveDateTime;
             }
+
+            @Override
+            public String getPlanPhaseName() {
+                return null;
+            }
         };
         return ev;
     }
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/alignment/PlanAligner.java b/entitlement/src/main/java/com/ning/billing/entitlement/alignment/PlanAligner.java
index 3593fea..534160a 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/alignment/PlanAligner.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/alignment/PlanAligner.java
@@ -79,7 +79,7 @@ public class PlanAligner {
                                                             final String priceList,
                                                             final DateTime requestedDate,
                                                             final DateTime effectiveDate) throws CatalogApiException, EntitlementUserApiException {
-        final List<TimedPhase> timedPhases = getTimedPhaseOnCreate(subscription.getStartDate(),
+        final List<TimedPhase> timedPhases = getTimedPhaseOnCreate(subscription.getAlignStartDate(),
                                                                    subscription.getBundleStartDate(),
                                                                    plan,
                                                                    initialPhase,
@@ -154,7 +154,8 @@ public class PlanAligner {
                 case MIGRATE_ENTITLEMENT:
                 case CREATE:
                 case RE_CREATE:
-                    final List<TimedPhase> timedPhases = getTimedPhaseOnCreate(subscription.getStartDate(),
+                case TRANSFER:
+                    final List<TimedPhase> timedPhases = getTimedPhaseOnCreate(subscription.getAlignStartDate(),
                                                                                subscription.getBundleStartDate(),
                                                                                lastPlanTransition.getNextPlan(),
                                                                                lastPlanTransition.getNextPhase().getPhaseType(),
@@ -163,7 +164,7 @@ public class PlanAligner {
                     return getTimedPhase(timedPhases, effectiveDate, WhichPhase.NEXT);
                 // If we went through Plan changes, borrow the logic for changePlan alignment
                 case CHANGE:
-                    return getTimedPhaseOnChange(subscription.getStartDate(),
+                    return getTimedPhaseOnChange(subscription.getAlignStartDate(),
                                                  subscription.getBundleStartDate(),
                                                  lastPlanTransition.getPreviousPhase(),
                                                  lastPlanTransition.getPreviousPlan(),
@@ -218,7 +219,7 @@ public class PlanAligner {
                                              final DateTime requestedDate,
                                              final DateTime effectiveDate,
                                              final WhichPhase which) throws CatalogApiException, EntitlementUserApiException {
-        return getTimedPhaseOnChange(subscription.getStartDate(),
+        return getTimedPhaseOnChange(subscription.getAlignStartDate(),
                                      subscription.getBundleStartDate(),
                                      subscription.getCurrentPhase(),
                                      subscription.getCurrentPlan(),
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/api/migration/DefaultEntitlementMigrationApi.java b/entitlement/src/main/java/com/ning/billing/entitlement/api/migration/DefaultEntitlementMigrationApi.java
index fa79bda..21f7e7a 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/api/migration/DefaultEntitlementMigrationApi.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/api/migration/DefaultEntitlementMigrationApi.java
@@ -140,7 +140,7 @@ public class DefaultEntitlementMigrationApi implements EntitlementMigrationApi {
                                                                                      .setBundleId(bundleId)
                                                                                      .setCategory(productCategory)
                                                                                      .setBundleStartDate(migrationStartDate)
-                                                                                     .setStartDate(migrationStartDate),
+                                                                                     .setAlignStartDate(migrationStartDate),
                                                                              emptyEvents);
         return new SubscriptionMigrationData(subscriptionData, toEvents(subscriptionData, now, ctd, events, context));
     }
@@ -156,7 +156,7 @@ public class DefaultEntitlementMigrationApi implements EntitlementMigrationApi {
                                                                                      .setBundleId(bundleId)
                                                                                      .setCategory(productCategory)
                                                                                      .setBundleStartDate(bundleStartDate)
-                                                                                     .setStartDate(migrationStartDate),
+                                                                                     .setAlignStartDate(migrationStartDate),
                                                                              emptyEvents);
         return new SubscriptionMigrationData(subscriptionData, toEvents(subscriptionData, now, ctd, events, context));
     }
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/api/timeline/DefaultEntitlementTimelineApi.java b/entitlement/src/main/java/com/ning/billing/entitlement/api/timeline/DefaultEntitlementTimelineApi.java
index 220c2ad..5e8cb20 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/api/timeline/DefaultEntitlementTimelineApi.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/api/timeline/DefaultEntitlementTimelineApi.java
@@ -1,4 +1,4 @@
-/* 
+/*
  * Copyright 2010-2011 Ning, Inc.
  *
  * Ning licenses this file to you under the Apache License, version 2.0
@@ -42,7 +42,6 @@ import com.ning.billing.entitlement.api.user.Subscription;
 import com.ning.billing.entitlement.api.user.SubscriptionBundle;
 import com.ning.billing.entitlement.api.user.SubscriptionBundleData;
 import com.ning.billing.entitlement.api.user.SubscriptionData;
-import com.ning.billing.entitlement.api.user.SubscriptionTransitionData;
 import com.ning.billing.entitlement.engine.dao.EntitlementDao;
 import com.ning.billing.entitlement.events.EntitlementEvent;
 import com.ning.billing.entitlement.glue.DefaultEntitlementModule;
@@ -62,7 +61,7 @@ public class DefaultEntitlementTimelineApi implements EntitlementTimelineApi {
 
     @Inject
     public DefaultEntitlementTimelineApi(@Named(DefaultEntitlementModule.REPAIR_NAMED) final SubscriptionFactory factory, final CatalogService catalogService,
-                                         @Named(DefaultEntitlementModule.REPAIR_NAMED) final RepairEntitlementLifecycleDao repairDao, final EntitlementDao dao) {
+            @Named(DefaultEntitlementModule.REPAIR_NAMED) final RepairEntitlementLifecycleDao repairDao, final EntitlementDao dao) {
         this.catalogService = catalogService;
         this.dao = dao;
         this.repairDao = repairDao;
@@ -70,24 +69,45 @@ public class DefaultEntitlementTimelineApi implements EntitlementTimelineApi {
     }
 
     @Override
-    public BundleTimeline getBundleRepair(final UUID bundleId) throws EntitlementRepairException {
+    public BundleTimeline getBundleTimeline(final SubscriptionBundle bundle)
+    throws EntitlementRepairException {
+        return getBundleTimelineInternal(bundle, bundle.getKey());
+    }
+
+
+    @Override
+    public BundleTimeline getBundleTimeline(final UUID accountId, final String bundleName)
+    throws EntitlementRepairException {
+        final SubscriptionBundle bundle = dao.getSubscriptionBundleFromAccountAndKey(accountId, bundleName);
+        return getBundleTimelineInternal(bundle, bundleName + " [accountId= " + accountId.toString() +"]");
+    }
+
+
+    @Override
+    public BundleTimeline getBundleTimeline(final UUID bundleId) throws EntitlementRepairException {
+
+        final SubscriptionBundle bundle = dao.getSubscriptionBundleFromId(bundleId);
+        return getBundleTimelineInternal(bundle, bundleId.toString());
+    }
+
+    private BundleTimeline getBundleTimelineInternal(final SubscriptionBundle bundle, String descBundle) throws EntitlementRepairException {
         try {
-            final SubscriptionBundle bundle = dao.getSubscriptionBundleFromId(bundleId);
             if (bundle == null) {
-                throw new EntitlementRepairException(ErrorCode.ENT_REPAIR_UNKNOWN_BUNDLE, bundleId);
+                throw new EntitlementRepairException(ErrorCode.ENT_REPAIR_UNKNOWN_BUNDLE, descBundle);
             }
-            final List<Subscription> subscriptions = dao.getSubscriptions(factory, bundleId);
+            final List<Subscription> subscriptions = dao.getSubscriptions(factory, bundle.getId());
             if (subscriptions.size() == 0) {
-                throw new EntitlementRepairException(ErrorCode.ENT_REPAIR_NO_ACTIVE_SUBSCRIPTIONS, bundleId);
+                throw new EntitlementRepairException(ErrorCode.ENT_REPAIR_NO_ACTIVE_SUBSCRIPTIONS, bundle.getId());
             }
             final String viewId = getViewId(((SubscriptionBundleData) bundle).getLastSysUpdateTime(), subscriptions);
             final List<SubscriptionTimeline> repairs = createGetSubscriptionRepairList(subscriptions, Collections.<SubscriptionTimeline>emptyList());
-            return createGetBundleRepair(bundleId, bundle.getKey(), viewId, repairs);
+            return createGetBundleRepair(bundle.getId(), bundle.getKey(), viewId, repairs);
         } catch (CatalogApiException e) {
             throw new EntitlementRepairException(e);
         }
     }
 
+
     @Override
     public BundleTimeline repairBundle(final BundleTimeline input, final boolean dryRun, final CallContext context) throws EntitlementRepairException {
         try {
@@ -124,7 +144,7 @@ public class DefaultEntitlementTimelineApi implements EntitlementTimelineApi {
 
                     final boolean isPlanRecreate = (curRepair.getNewEvents().size() > 0
                             && (curRepair.getNewEvents().get(0).getSubscriptionTransitionType() == SubscriptionTransitionType.CREATE
-                            || curRepair.getNewEvents().get(0).getSubscriptionTransitionType() == SubscriptionTransitionType.RE_CREATE));
+                                    || curRepair.getNewEvents().get(0).getSubscriptionTransitionType() == SubscriptionTransitionType.RE_CREATE));
 
                     final DateTime newSubscriptionStartDate = isPlanRecreate ? curRepair.getNewEvents().get(0).getRequestedDate() : null;
 
@@ -156,7 +176,7 @@ public class DefaultEntitlementTimelineApi implements EntitlementTimelineApi {
                     inRepair.add(curOutputRepair);
                     if (curOutputRepair.getCategory() == ProductCategory.ADD_ON) {
                         // Check if ADD_ON RE_CREATE is before BP start
-                        if (isPlanRecreate && subscriptions.get(0).getStartDate().isAfter(curRepair.getNewEvents().get(0).getRequestedDate())) {
+                        if (isPlanRecreate && (subscriptions.get(0)).getStartDate().isAfter(curRepair.getNewEvents().get(0).getRequestedDate())) {
                             throw new EntitlementRepairException(ErrorCode.ENT_REPAIR_AO_CREATE_BEFORE_BP_START, cur.getId(), cur.getBundleId());
                         }
                         addOnSubscriptionInRepair.add(curOutputRepair);
@@ -168,25 +188,25 @@ public class DefaultEntitlementTimelineApi implements EntitlementTimelineApi {
 
             final RepairType repairType = getRepairType(subscriptions.get(0), (baseSubscriptionRepair != null));
             switch (repairType) {
-                case BASE_REPAIR:
-                    // We need to add any existing addon that are not in the input repair list
-                    for (final Subscription cur : subscriptions) {
-                        if (cur.getCategory() == ProductCategory.ADD_ON && !inRepair.contains(cur)) {
-                            final SubscriptionDataRepair curOutputRepair = createSubscriptionDataRepair((SubscriptionDataRepair) cur, newBundleStartDate, null, ((SubscriptionDataRepair) cur).getEvents());
-                            repairDao.initializeRepair(curOutputRepair.getId(), ((SubscriptionDataRepair) cur).getEvents());
-                            inRepair.add(curOutputRepair);
-                            addOnSubscriptionInRepair.add(curOutputRepair);
-                        }
+            case BASE_REPAIR:
+                // We need to add any existing addon that are not in the input repair list
+                for (final Subscription cur : subscriptions) {
+                    if (cur.getCategory() == ProductCategory.ADD_ON && !inRepair.contains(cur)) {
+                        final SubscriptionDataRepair curOutputRepair = createSubscriptionDataRepair((SubscriptionDataRepair) cur, newBundleStartDate, null, ((SubscriptionDataRepair) cur).getEvents());
+                        repairDao.initializeRepair(curOutputRepair.getId(), ((SubscriptionDataRepair) cur).getEvents());
+                        inRepair.add(curOutputRepair);
+                        addOnSubscriptionInRepair.add(curOutputRepair);
                     }
-                    break;
-                case ADD_ON_REPAIR:
-                    // We need to set the baseSubscription as it is useful to calculate addon validity
-                    final SubscriptionDataRepair baseSubscription = (SubscriptionDataRepair) subscriptions.get(0);
-                    baseSubscriptionRepair = createSubscriptionDataRepair(baseSubscription, baseSubscription.getBundleStartDate(), baseSubscription.getStartDate(), baseSubscription.getEvents());
-                    break;
-                case STANDALONE_REPAIR:
-                default:
-                    break;
+                }
+                break;
+            case ADD_ON_REPAIR:
+                // We need to set the baseSubscription as it is useful to calculate addon validity
+                final SubscriptionDataRepair baseSubscription = (SubscriptionDataRepair) subscriptions.get(0);
+                baseSubscriptionRepair = createSubscriptionDataRepair(baseSubscription, baseSubscription.getBundleStartDate(), baseSubscription.getAlignStartDate(), baseSubscription.getEvents());
+                break;
+            case STANDALONE_REPAIR:
+            default:
+                break;
             }
 
             validateBasePlanRecreate(isBasePlanRecreate, subscriptions, input.getSubscriptions());
@@ -209,7 +229,7 @@ public class DefaultEntitlementTimelineApi implements EntitlementTimelineApi {
                 return createGetBundleRepair(input.getBundleId(), bundle.getKey(), input.getViewId(), repairs);
             } else {
                 dao.repair(bundle.getAccountId(), input.getBundleId(), inRepair, context);
-                return getBundleRepair(input.getBundleId());
+                return getBundleTimeline(input.getBundleId());
             }
         } catch (CatalogApiException e) {
             throw new EntitlementRepairException(e);
@@ -227,7 +247,7 @@ public class DefaultEntitlementTimelineApi implements EntitlementTimelineApi {
     }
 
     private void validateBasePlanRecreate(final boolean isBasePlanRecreate, final List<Subscription> subscriptions, final List<SubscriptionTimeline> input)
-            throws EntitlementRepairException {
+    throws EntitlementRepairException {
         if (!isBasePlanRecreate) {
             return;
         }
@@ -237,14 +257,14 @@ public class DefaultEntitlementTimelineApi implements EntitlementTimelineApi {
         for (final SubscriptionTimeline cur : input) {
             if (cur.getNewEvents().size() != 0
                     && (cur.getNewEvents().get(0).getSubscriptionTransitionType() != SubscriptionTransitionType.CREATE
-                    && cur.getNewEvents().get(0).getSubscriptionTransitionType() != SubscriptionTransitionType.RE_CREATE)) {
+                            && cur.getNewEvents().get(0).getSubscriptionTransitionType() != SubscriptionTransitionType.RE_CREATE)) {
                 throw new EntitlementRepairException(ErrorCode.ENT_REPAIR_BP_RECREATE_MISSING_AO_CREATE, subscriptions.get(0).getBundleId());
             }
         }
     }
 
     private void validateInputSubscriptionsKnown(final List<Subscription> subscriptions, final List<SubscriptionTimeline> input)
-            throws EntitlementRepairException {
+    throws EntitlementRepairException {
         for (final SubscriptionTimeline cur : input) {
             boolean found = false;
             for (final Subscription s : subscriptions) {
@@ -260,7 +280,7 @@ public class DefaultEntitlementTimelineApi implements EntitlementTimelineApi {
     }
 
     private void validateFirstNewEvent(final SubscriptionData data, final NewEvent firstNewEvent, final DateTime lastBPRemainingTime, final DateTime lastRemainingTime)
-            throws EntitlementRepairException {
+    throws EntitlementRepairException {
         if (lastBPRemainingTime != null &&
                 firstNewEvent.getRequestedDate().isBefore(lastBPRemainingTime)) {
             throw new EntitlementRepairException(ErrorCode.ENT_REPAIR_NEW_EVENT_BEFORE_LAST_BP_REMAINING, firstNewEvent.getSubscriptionTransitionType(), data.getId());
@@ -289,7 +309,7 @@ public class DefaultEntitlementTimelineApi implements EntitlementTimelineApi {
     }
 
     private List<EntitlementEvent> getRemainingEventsAndValidateDeletedEvents(final SubscriptionDataRepair data, final DateTime firstBPDeletedTime,
-                                                                              final List<SubscriptionTimeline.DeletedEvent> deletedEvents)
+            final List<SubscriptionTimeline.DeletedEvent> deletedEvents)
             throws EntitlementRepairException {
         if (deletedEvents == null || deletedEvents.size() == 0) {
             return data.getEvents();
@@ -420,7 +440,7 @@ public class DefaultEntitlementTimelineApi implements EntitlementTimelineApi {
             builder.setBundleStartDate(newBundleStartDate);
         }
         if (newSubscriptionStartDate != null) {
-            builder.setStartDate(newSubscriptionStartDate);
+            builder.setAlignStartDate(newSubscriptionStartDate);
         }
         if (initialEvents.size() > 0) {
             for (final EntitlementEvent cur : initialEvents) {
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/api/timeline/DefaultSubscriptionTimeline.java b/entitlement/src/main/java/com/ning/billing/entitlement/api/timeline/DefaultSubscriptionTimeline.java
index f9a17f3..11d2a01 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/api/timeline/DefaultSubscriptionTimeline.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/api/timeline/DefaultSubscriptionTimeline.java
@@ -1,4 +1,4 @@
-/* 
+/*
  * Copyright 2010-2011 Ning, Inc.
  *
  * Ning licenses this file to you under the Apache License, version 2.0
@@ -44,9 +44,11 @@ public class DefaultSubscriptionTimeline implements SubscriptionTimeline {
     private final List<ExistingEvent> existingEvents;
     private final List<NewEvent> newEvents;
     private final List<DeletedEvent> deletedEvents;
+    private final long activeVersion;
 
-    public DefaultSubscriptionTimeline(final UUID id) {
+    public DefaultSubscriptionTimeline(final UUID id, final long activeVersion) {
         this.id = id;
+        this.activeVersion = activeVersion;
         this.existingEvents = Collections.<SubscriptionTimeline.ExistingEvent>emptyList();
         this.deletedEvents = Collections.<SubscriptionTimeline.DeletedEvent>emptyList();
         this.newEvents = Collections.<SubscriptionTimeline.NewEvent>emptyList();
@@ -54,6 +56,7 @@ public class DefaultSubscriptionTimeline implements SubscriptionTimeline {
 
     public DefaultSubscriptionTimeline(final SubscriptionTimeline input) {
         this.id = input.getId();
+        this.activeVersion = input.getActiveVersion();
         this.existingEvents = (input.getExistingEvents() != null) ? new ArrayList<SubscriptionTimeline.ExistingEvent>(input.getExistingEvents()) :
                 Collections.<SubscriptionTimeline.ExistingEvent>emptyList();
         sortExistingEvent(this.existingEvents);
@@ -70,6 +73,7 @@ public class DefaultSubscriptionTimeline implements SubscriptionTimeline {
         this.existingEvents = toExistingEvents(catalog, input.getActiveVersion(), input.getCategory(), input.getEvents());
         this.deletedEvents = null;
         this.newEvents = null;
+        this.activeVersion = input.getActiveVersion();
     }
 
     private List<ExistingEvent> toExistingEvents(final Catalog catalog, final long activeVersion, final ProductCategory category, final List<EntitlementEvent> events)
@@ -100,11 +104,13 @@ public class DefaultSubscriptionTimeline implements SubscriptionTimeline {
             BillingPeriod billingPeriod = null;
             String priceListName = null;
             PhaseType phaseType = null;
+            String planPhaseName = null;
 
             ApiEventType apiType = null;
             switch (cur.getType()) {
                 case PHASE:
                     final PhaseEvent phaseEV = (PhaseEvent) cur;
+                    planPhaseName = phaseEV.getPhase();
                     phaseType = catalog.findPhase(phaseEV.getPhase(), cur.getEffectiveDate(), startDate).getPhaseType();
                     productName = prevProductName;
                     billingPeriod = catalog.findPhase(phaseEV.getPhase(), cur.getEffectiveDate(), startDate).getBillingPeriod();
@@ -114,6 +120,7 @@ public class DefaultSubscriptionTimeline implements SubscriptionTimeline {
                 case API_USER:
                     final ApiEvent userEV = (ApiEvent) cur;
                     apiType = userEV.getEventType();
+                    planPhaseName = userEV.getEventPlanPhase();
                     final Plan plan = (userEV.getEventPlan() != null) ? catalog.findPlan(userEV.getEventPlan(), cur.getRequestedDate(), startDate) : null;
                     phaseType = (userEV.getEventPlanPhase() != null) ? catalog.findPhase(userEV.getEventPlanPhase(), cur.getEffectiveDate(), startDate).getPhaseType() : prevPhaseType;
                     productName = (plan != null) ? plan.getProduct().getName() : prevProductName;
@@ -124,6 +131,7 @@ public class DefaultSubscriptionTimeline implements SubscriptionTimeline {
 
             final SubscriptionTransitionType transitionType = SubscriptionTransitionData.toSubscriptionTransitionType(cur.getType(), apiType);
 
+            final String planPhaseNameWithClosure = planPhaseName;
             final PlanPhaseSpecifier spec = new PlanPhaseSpecifier(productName, category, billingPeriod, priceListName, phaseType);
             result.add(new ExistingEvent() {
                 @Override
@@ -150,6 +158,11 @@ public class DefaultSubscriptionTimeline implements SubscriptionTimeline {
                 public DateTime getEffectiveDate() {
                     return cur.getEffectiveDate();
                 }
+
+                @Override
+                public String getPlanPhaseName() {
+                    return planPhaseNameWithClosure;
+                }
             });
 
             prevProductName = productName;
@@ -265,6 +278,12 @@ public class DefaultSubscriptionTimeline implements SubscriptionTimeline {
         return existingEvents;
     }
 
+    @Override
+    public long getActiveVersion() {
+        return activeVersion;
+    }
+
+
     private void sortExistingEvent(final List<ExistingEvent> events) {
         if (events != null) {
             Collections.sort(events, new Comparator<ExistingEvent>() {
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/api/transfer/DefaultEntitlementTransferApi.java b/entitlement/src/main/java/com/ning/billing/entitlement/api/transfer/DefaultEntitlementTransferApi.java
new file mode 100644
index 0000000..6e3a0a9
--- /dev/null
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/api/transfer/DefaultEntitlementTransferApi.java
@@ -0,0 +1,282 @@
+/*
+ * Copyright 2010-2011 Ning, Inc.
+ *
+ * Ning 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 com.ning.billing.entitlement.api.transfer;
+
+
+import java.util.LinkedList;
+import java.util.List;
+import java.util.UUID;
+
+import org.joda.time.DateTime;
+
+import com.google.common.collect.ImmutableList;
+import com.google.inject.Inject;
+import com.ning.billing.ErrorCode;
+import com.ning.billing.catalog.api.Catalog;
+import com.ning.billing.catalog.api.CatalogApiException;
+import com.ning.billing.catalog.api.CatalogService;
+import com.ning.billing.catalog.api.Plan;
+import com.ning.billing.catalog.api.PlanPhase;
+import com.ning.billing.catalog.api.PlanPhaseSpecifier;
+import com.ning.billing.catalog.api.ProductCategory;
+import com.ning.billing.entitlement.api.SubscriptionFactory;
+import com.ning.billing.entitlement.api.SubscriptionTransitionType;
+import com.ning.billing.entitlement.api.migration.AccountMigrationData.BundleMigrationData;
+import com.ning.billing.entitlement.api.migration.AccountMigrationData.SubscriptionMigrationData;
+import com.ning.billing.entitlement.api.timeline.BundleTimeline;
+import com.ning.billing.entitlement.api.timeline.EntitlementRepairException;
+import com.ning.billing.entitlement.api.timeline.EntitlementTimelineApi;
+import com.ning.billing.entitlement.api.timeline.SubscriptionTimeline;
+import com.ning.billing.entitlement.api.timeline.SubscriptionTimeline.ExistingEvent;
+import com.ning.billing.entitlement.api.user.SubscriptionBundle;
+import com.ning.billing.entitlement.api.user.SubscriptionBundleData;
+import com.ning.billing.entitlement.api.user.SubscriptionData;
+import com.ning.billing.entitlement.api.user.DefaultSubscriptionFactory.SubscriptionBuilder;
+import com.ning.billing.entitlement.engine.dao.EntitlementDao;
+import com.ning.billing.entitlement.events.EntitlementEvent;
+import com.ning.billing.entitlement.events.phase.PhaseEventData;
+import com.ning.billing.entitlement.events.user.ApiEventBuilder;
+import com.ning.billing.entitlement.events.user.ApiEventCancel;
+import com.ning.billing.entitlement.events.user.ApiEventChange;
+import com.ning.billing.entitlement.events.user.ApiEventCreate;
+import com.ning.billing.entitlement.events.user.ApiEventTransfer;
+
+import com.ning.billing.entitlement.exceptions.EntitlementError;
+import com.ning.billing.util.callcontext.CallContext;
+import com.ning.billing.util.clock.Clock;
+
+public class DefaultEntitlementTransferApi implements EntitlementTransferApi {
+
+
+    private final Clock clock;
+    private final EntitlementDao dao;
+    private final CatalogService catalogService;
+    private final SubscriptionFactory subscriptionFactory;
+    private final EntitlementTimelineApi timelineApi;
+
+    @Inject
+    public DefaultEntitlementTransferApi(final Clock clock, final EntitlementDao dao, final EntitlementTimelineApi timelineApi, final CatalogService catalogService,
+            final SubscriptionFactory subscriptionFactory) {
+        this.clock = clock;
+        this.dao = dao;
+        this.catalogService = catalogService;
+        this.subscriptionFactory = subscriptionFactory;
+        this.timelineApi = timelineApi;
+    }
+
+
+    private EntitlementEvent createEvent(final boolean firstEvent, final ExistingEvent existingEvent, final SubscriptionData subscription, final DateTime transferDate, final CallContext context)
+    throws CatalogApiException {
+
+        EntitlementEvent newEvent = null;
+
+        final Catalog catalog = catalogService.getFullCatalog();
+
+
+        final DateTime effectiveDate = existingEvent.getEffectiveDate().isBefore(transferDate) ? transferDate : existingEvent.getEffectiveDate();
+
+        // STEPH Issue with catalog for NO_BILLING_PERIOD
+        /*
+        final PlanPhaseSpecifier spec = existingEvent.getPlanPhaseSpecifier();
+        final Plan currentPlan = catalog.findPlan(spec.getProductName(), spec.getBillingPeriod(), spec.getPriceListName(), existingEvent.getEffectiveDate());
+        PlanPhase currentPhase = null;
+        for (PlanPhase curPhase : currentPlan.getAllPhases()) {
+            if (curPhase.getPhaseType() == spec.getPhaseType()) {
+                currentPhase = curPhase;
+                break;
+            }
+        }
+
+        *
+        */
+
+        final PlanPhaseSpecifier spec = existingEvent.getPlanPhaseSpecifier();
+        final PlanPhase currentPhase = catalog.findPhase(existingEvent.getPlanPhaseName(), effectiveDate, subscription.getAlignStartDate());
+
+
+        final ApiEventBuilder apiBuilder = new ApiEventBuilder()
+        .setSubscriptionId(subscription.getId())
+        .setEventPlan(currentPhase.getPlan().getName())
+        .setEventPlanPhase(currentPhase.getName())
+        .setEventPriceList(spec.getPriceListName())
+        .setActiveVersion(subscription.getActiveVersion())
+        .setProcessedDate(clock.getUTCNow())
+        .setEffectiveDate(effectiveDate)
+        .setRequestedDate(effectiveDate)
+        .setUserToken(context.getUserToken())
+        .setFromDisk(true);
+
+        switch(existingEvent.getSubscriptionTransitionType()) {
+        case MIGRATE_ENTITLEMENT:
+        case RE_CREATE:
+        case CREATE:
+            newEvent = new ApiEventTransfer(apiBuilder);
+            break;
+
+        // Should we even keep future change events; product question really
+        case CHANGE:
+            newEvent = firstEvent ? new ApiEventTransfer(apiBuilder) : new ApiEventChange(apiBuilder);
+            break;
+
+        case PHASE:
+            newEvent = firstEvent ? new ApiEventTransfer(apiBuilder) :
+                    PhaseEventData.createNextPhaseEvent(currentPhase.getName(), subscription, clock.getUTCNow(), effectiveDate);
+            break;
+
+        // Ignore
+        case CANCEL:
+        case UNCANCEL:
+        case MIGRATE_BILLING:
+            break;
+        default:
+            throw new EntitlementError(String.format("Unepxected transitionType %s", existingEvent.getSubscriptionTransitionType()));
+        }
+        return newEvent;
+    }
+
+
+    private List<EntitlementEvent> toEvents(final List<ExistingEvent> existingEvents, final SubscriptionData subscription,
+            final DateTime transferDate, final CallContext context) throws EntitlementTransferApiException {
+
+        try {
+            final List<EntitlementEvent> result = new LinkedList<EntitlementEvent>();
+
+            EntitlementEvent event = null;
+            ExistingEvent prevEvent = null;
+            boolean firstEvent = true;
+            for (ExistingEvent cur : existingEvents) {
+                // Skip all events prior to the transferDate
+                if (cur.getEffectiveDate().isBefore(transferDate)) {
+                    prevEvent = cur;
+                    continue;
+                }
+
+                // Add previous event the first time if needed
+                if (prevEvent != null) {
+                    event = createEvent(firstEvent, prevEvent, subscription, transferDate, context);
+                    if (event != null) {
+                        result.add(event);
+                        firstEvent = false;
+                    }
+                    prevEvent = null;
+                }
+
+                event = createEvent(firstEvent, cur, subscription, transferDate, context);
+                if (event != null) {
+                    result.add(event);
+                    firstEvent = false;
+                }
+            }
+
+            // Previous loop did not get anything because transferDate is greater than effectiveDate of last event
+            if (prevEvent != null) {
+                event = createEvent(firstEvent, prevEvent, subscription, transferDate, context);
+                if (event != null) {
+                    result.add(event);
+                }
+                prevEvent = null;
+            }
+
+
+            return result;
+        } catch (CatalogApiException e) {
+            throw new EntitlementTransferApiException(e);
+        }
+    }
+
+
+    @Override
+    public void transferBundle(final UUID sourceAccountId, final UUID destAccountId,
+            final String bundleKey, final DateTime transferDate, final boolean transferAddOn,
+            final CallContext context) throws EntitlementTransferApiException {
+
+        try {
+
+            final SubscriptionBundle bundle = dao.getSubscriptionBundleFromAccountAndKey(sourceAccountId, bundleKey);
+            if (bundle == null) {
+                throw new EntitlementTransferApiException(ErrorCode.ENT_CREATE_NO_BUNDLE, bundleKey);
+            }
+
+            // Get the bundle timeline for the old account
+            final BundleTimeline bundleTimeline = timelineApi.getBundleTimeline(bundle);
+
+            final SubscriptionBundleData subscriptionBundleData = new SubscriptionBundleData(bundleKey, destAccountId, transferDate);
+            final List<SubscriptionMigrationData> subscriptionMigrationDataList = new LinkedList<SubscriptionMigrationData>();
+
+            final List<TransferCancelData> transferCancelDataList = new LinkedList<TransferCancelData>();
+
+            DateTime bundleStartdate = null;
+
+
+            for (SubscriptionTimeline cur : bundleTimeline.getSubscriptions()) {
+
+                final SubscriptionData oldSubscription = (SubscriptionData) dao.getSubscriptionFromId(subscriptionFactory, cur.getId());
+                final List<ExistingEvent> existingEvents = cur.getExistingEvents();
+                final ProductCategory productCategory = existingEvents.get(0).getPlanPhaseSpecifier().getProductCategory();
+                if (productCategory == ProductCategory.ADD_ON) {
+                    if (!transferAddOn) {
+                        continue;
+                    }
+                } else {
+
+
+
+                    // If BP or STANDALONE subscription, create the cancel event on effectiveCancelDate
+                    final DateTime effectiveCancelDate = oldSubscription.getChargedThroughDate() != null && transferDate.isBefore(oldSubscription.getChargedThroughDate()) ?
+                            oldSubscription.getChargedThroughDate() : transferDate;
+
+                            final EntitlementEvent cancelEvent = new ApiEventCancel(new ApiEventBuilder()
+                            .setSubscriptionId(cur.getId())
+                            .setActiveVersion(cur.getActiveVersion())
+                            .setProcessedDate(clock.getUTCNow())
+                            .setEffectiveDate(effectiveCancelDate)
+                            .setRequestedDate(transferDate)
+                            .setUserToken(context.getUserToken())
+                            .setFromDisk(true));
+
+                            TransferCancelData cancelData =  new TransferCancelData(oldSubscription, cancelEvent);
+                            transferCancelDataList.add(cancelData);
+                }
+
+                // We Align with the original subscription
+                final DateTime subscriptionAlignStartDate = oldSubscription.getAlignStartDate();
+                if (bundleStartdate == null) {
+                    bundleStartdate = oldSubscription.getStartDate();
+                }
+
+                // Create the new subscription for the new bundle on the new account
+                final SubscriptionData subscriptionData = subscriptionFactory.createSubscription(new SubscriptionBuilder()
+                .setId(UUID.randomUUID())
+                .setBundleId(subscriptionBundleData.getId())
+                .setCategory(productCategory)
+                .setBundleStartDate(transferDate)
+                .setAlignStartDate(subscriptionAlignStartDate),
+                ImmutableList.<EntitlementEvent>of());
+
+                final List<EntitlementEvent> events = toEvents(existingEvents, subscriptionData, transferDate, context);
+                final SubscriptionMigrationData curData = new SubscriptionMigrationData(subscriptionData, events);
+                subscriptionMigrationDataList.add(curData);
+            }
+            BundleMigrationData bundleMigrationData = new BundleMigrationData(subscriptionBundleData, subscriptionMigrationDataList);
+
+            // Atomically cancel all subscription on old account and create new bundle, subscriptions, events for new account
+            dao.transfer(sourceAccountId, destAccountId, bundleMigrationData, transferCancelDataList, context);
+
+        } catch (EntitlementRepairException e) {
+            throw new EntitlementTransferApiException(e);
+        }
+    }
+}
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/api/transfer/TransferCancelData.java b/entitlement/src/main/java/com/ning/billing/entitlement/api/transfer/TransferCancelData.java
new file mode 100644
index 0000000..86f05ae
--- /dev/null
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/api/transfer/TransferCancelData.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2010-2011 Ning, Inc.
+ *
+ * Ning 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 com.ning.billing.entitlement.api.transfer;
+
+import com.ning.billing.entitlement.api.user.SubscriptionData;
+import com.ning.billing.entitlement.events.EntitlementEvent;
+
+public class TransferCancelData {
+
+    final SubscriptionData subscription;
+    final EntitlementEvent cancelEvent;
+
+    public TransferCancelData(final SubscriptionData subscription,
+            final EntitlementEvent cancelEvent) {
+        this.subscription = subscription;
+        this.cancelEvent = cancelEvent;
+    }
+
+    public SubscriptionData getSubscription() {
+        return subscription;
+    }
+
+    public EntitlementEvent getCancelEvent() {
+        return cancelEvent;
+    }
+}
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/api/user/DefaultEntitlementUserApi.java b/entitlement/src/main/java/com/ning/billing/entitlement/api/user/DefaultEntitlementUserApi.java
index f63cf6e..11f6bef 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/api/user/DefaultEntitlementUserApi.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/api/user/DefaultEntitlementUserApi.java
@@ -56,7 +56,6 @@ public class DefaultEntitlementUserApi implements EntitlementUserApi {
     @Inject
     public DefaultEntitlementUserApi(final Clock clock, final EntitlementDao dao, final CatalogService catalogService,
                                      final DefaultSubscriptionApiService apiService, final SubscriptionFactory subscriptionFactory, final AddonUtils addonUtils) {
-        super();
         this.clock = clock;
         this.apiService = apiService;
         this.dao = dao;
@@ -84,8 +83,8 @@ public class DefaultEntitlementUserApi implements EntitlementUserApi {
     }
 
     @Override
-    public SubscriptionBundle getBundleForKey(final String bundleKey) throws EntitlementUserApiException {
-        final SubscriptionBundle result = dao.getSubscriptionBundleFromKey(bundleKey);
+    public SubscriptionBundle getBundleForAccountAndKey(final UUID accountId, final String bundleKey) throws EntitlementUserApiException {
+        final SubscriptionBundle result = dao.getSubscriptionBundleFromAccountAndKey(accountId, bundleKey);
         if (result == null) {
             throw new EntitlementUserApiException(ErrorCode.ENT_GET_INVALID_BUNDLE_KEY, bundleKey);
         }
@@ -93,13 +92,19 @@ public class DefaultEntitlementUserApi implements EntitlementUserApi {
     }
 
     @Override
+    public List<SubscriptionBundle> getBundlesForKey(final String bundleKey)
+    throws EntitlementUserApiException {
+        return dao.getSubscriptionBundlesForKey(bundleKey);
+    }
+
+    @Override
     public List<SubscriptionBundle> getBundlesForAccount(final UUID accountId) {
         return dao.getSubscriptionBundleForAccount(accountId);
     }
 
     @Override
-    public List<Subscription> getSubscriptionsForKey(final String bundleKey) {
-        return dao.getSubscriptionsForKey(subscriptionFactory, bundleKey);
+    public List<Subscription> getSubscriptionsForAccountAndKey(final UUID accountId, final String bundleKey) {
+        return dao.getSubscriptionsForAccountAndKey(subscriptionFactory, accountId, bundleKey);
     }
 
     @Override
@@ -116,6 +121,7 @@ public class DefaultEntitlementUserApi implements EntitlementUserApi {
         return result;
     }
 
+    @Override
     public SubscriptionBundle createBundleForAccount(final UUID accountId, final String bundleName, final CallContext context)
             throws EntitlementUserApiException {
         final SubscriptionBundleData bundle = new SubscriptionBundleData(bundleName, accountId, clock.getUTCNow());
@@ -190,7 +196,7 @@ public class DefaultEntitlementUserApi implements EntitlementUserApi {
                                                  .setBundleId(bundleId)
                                                  .setCategory(plan.getProduct().getCategory())
                                                  .setBundleStartDate(bundleStartDate)
-                                                 .setStartDate(effectiveDate),
+                                                 .setAlignStartDate(effectiveDate),
                                          plan, spec.getPhaseType(), realPriceList, requestedDate, effectiveDate, now, context);
         } catch (CatalogApiException e) {
             throw new EntitlementUserApiException(e);
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/api/user/DefaultSubscriptionApiService.java b/entitlement/src/main/java/com/ning/billing/entitlement/api/user/DefaultSubscriptionApiService.java
index da61996..ac3e704 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/api/user/DefaultSubscriptionApiService.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/api/user/DefaultSubscriptionApiService.java
@@ -121,17 +121,17 @@ public class DefaultSubscriptionApiService implements SubscriptionApiService {
             final TimedPhase[] curAndNextPhases = planAligner.getCurrentAndNextTimedPhaseOnCreate(subscription, plan, initialPhase, realPriceList, requestedDate, effectiveDate);
 
             final ApiEventBuilder createBuilder = new ApiEventBuilder()
-                    .setSubscriptionId(subscription.getId())
-                    .setEventPlan(plan.getName())
-                    .setEventPlanPhase(curAndNextPhases[0].getPhase().getName())
-                    .setEventPriceList(realPriceList)
-                    .setActiveVersion(subscription.getActiveVersion())
-                    .setProcessedDate(processedDate)
-                    .setEffectiveDate(effectiveDate)
-                    .setRequestedDate(requestedDate)
-                    .setUserToken(context.getUserToken())
-                    .setFromDisk(true);
-            final ApiEvent creationEvent = (reCreate) ? new ApiEventReCreate(createBuilder) : new ApiEventCreate(createBuilder);
+            .setSubscriptionId(subscription.getId())
+            .setEventPlan(plan.getName())
+            .setEventPlanPhase(curAndNextPhases[0].getPhase().getName())
+            .setEventPriceList(realPriceList)
+            .setActiveVersion(subscription.getActiveVersion())
+            .setProcessedDate(processedDate)
+            .setEffectiveDate(effectiveDate)
+            .setRequestedDate(requestedDate)
+            .setUserToken(context.getUserToken())
+            .setFromDisk(true);
+    final ApiEvent creationEvent = (reCreate) ? new ApiEventReCreate(createBuilder) : new ApiEventCreate(createBuilder);
 
             final TimedPhase nextTimedPhase = curAndNextPhases[1];
             final PhaseEvent nextPhaseEvent = (nextTimedPhase != null) ?
@@ -176,13 +176,13 @@ public class DefaultSubscriptionApiService implements SubscriptionApiService {
             final DateTime effectiveDate = subscription.getPlanChangeEffectiveDate(policy, requestedDate);
 
             final EntitlementEvent cancelEvent = new ApiEventCancel(new ApiEventBuilder()
-                                                                            .setSubscriptionId(subscription.getId())
-                                                                            .setActiveVersion(subscription.getActiveVersion())
-                                                                            .setProcessedDate(now)
-                                                                            .setEffectiveDate(effectiveDate)
-                                                                            .setRequestedDate(requestedDate)
-                                                                            .setUserToken(context.getUserToken())
-                                                                            .setFromDisk(true));
+            .setSubscriptionId(subscription.getId())
+            .setActiveVersion(subscription.getActiveVersion())
+            .setProcessedDate(now)
+            .setEffectiveDate(effectiveDate)
+            .setRequestedDate(requestedDate)
+            .setUserToken(context.getUserToken())
+            .setFromDisk(true));
 
             dao.cancelSubscription(subscription, cancelEvent, context, 0);
             subscription.rebuildTransitions(dao.getEventsForSubscription(subscription.getId()), catalogService.getFullCatalog());
@@ -300,16 +300,16 @@ public class DefaultSubscriptionApiService implements SubscriptionApiService {
         final TimedPhase currentTimedPhase = planAligner.getCurrentTimedPhaseOnChange(subscription, newPlan, newPriceList.getName(), requestedDate, effectiveDate);
 
         final EntitlementEvent changeEvent = new ApiEventChange(new ApiEventBuilder()
-                                                                        .setSubscriptionId(subscription.getId())
-                                                                        .setEventPlan(newPlan.getName())
-                                                                        .setEventPlanPhase(currentTimedPhase.getPhase().getName())
-                                                                        .setEventPriceList(newPriceList.getName())
-                                                                        .setActiveVersion(subscription.getActiveVersion())
-                                                                        .setProcessedDate(now)
-                                                                        .setEffectiveDate(effectiveDate)
-                                                                        .setRequestedDate(requestedDate)
-                                                                        .setUserToken(context.getUserToken())
-                                                                        .setFromDisk(true));
+        .setSubscriptionId(subscription.getId())
+        .setEventPlan(newPlan.getName())
+        .setEventPlanPhase(currentTimedPhase.getPhase().getName())
+        .setEventPriceList(newPriceList.getName())
+        .setActiveVersion(subscription.getActiveVersion())
+        .setProcessedDate(now)
+        .setEffectiveDate(effectiveDate)
+        .setRequestedDate(requestedDate)
+        .setUserToken(context.getUserToken())
+        .setFromDisk(true));
 
         final TimedPhase nextTimedPhase = planAligner.getNextTimedPhaseOnChange(subscription, newPlan, newPriceList.getName(), requestedDate, effectiveDate);
         final PhaseEvent nextPhaseEvent = (nextTimedPhase != null) ?
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/api/user/DefaultSubscriptionFactory.java b/entitlement/src/main/java/com/ning/billing/entitlement/api/user/DefaultSubscriptionFactory.java
index 8bd7471..1e1ebeb 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/api/user/DefaultSubscriptionFactory.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/api/user/DefaultSubscriptionFactory.java
@@ -43,6 +43,7 @@ public class DefaultSubscriptionFactory implements SubscriptionFactory {
         this.catalogService = catalogService;
     }
 
+    @Override
     public SubscriptionData createSubscription(final SubscriptionBuilder builder, final List<EntitlementEvent> events) {
         final SubscriptionData subscription = new SubscriptionData(builder, apiService, clock);
         if (events.size() > 0) {
@@ -54,7 +55,7 @@ public class DefaultSubscriptionFactory implements SubscriptionFactory {
     public static class SubscriptionBuilder {
         private UUID id;
         private UUID bundleId;
-        private DateTime startDate;
+        private DateTime alignStartDate;
         private DateTime bundleStartDate;
         private Long activeVersion;
         private ProductCategory category;
@@ -68,7 +69,7 @@ public class DefaultSubscriptionFactory implements SubscriptionFactory {
         public SubscriptionBuilder(final SubscriptionData original) {
             this.id = original.getId();
             this.bundleId = original.getBundleId();
-            this.startDate = original.getStartDate();
+            this.alignStartDate = original.getAlignStartDate();
             this.bundleStartDate = original.getBundleStartDate();
             this.category = original.getCategory();
             this.activeVersion = original.getActiveVersion();
@@ -86,8 +87,8 @@ public class DefaultSubscriptionFactory implements SubscriptionFactory {
             return this;
         }
 
-        public SubscriptionBuilder setStartDate(final DateTime startDate) {
-            this.startDate = startDate;
+        public SubscriptionBuilder setAlignStartDate(final DateTime alignStartDate) {
+            this.alignStartDate = alignStartDate;
             return this;
         }
 
@@ -124,8 +125,8 @@ public class DefaultSubscriptionFactory implements SubscriptionFactory {
             return bundleId;
         }
 
-        public DateTime getStartDate() {
-            return startDate;
+        public DateTime getAlignStartDate() {
+            return alignStartDate;
         }
 
         public DateTime getBundleStartDate() {
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/api/user/SubscriptionData.java b/entitlement/src/main/java/com/ning/billing/entitlement/api/user/SubscriptionData.java
index d7b8a78..fe7d666 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/api/user/SubscriptionData.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/api/user/SubscriptionData.java
@@ -63,7 +63,7 @@ public class SubscriptionData extends EntityBase implements Subscription {
     // Final subscription fields
     //
     private final UUID bundleId;
-    private final DateTime startDate;
+    private final DateTime alignStartDate;
     private final DateTime bundleStartDate;
     private final ProductCategory category;
 
@@ -88,12 +88,12 @@ public class SubscriptionData extends EntityBase implements Subscription {
     }
 
     public SubscriptionData(final SubscriptionBuilder builder,
-                            @Nullable final SubscriptionApiService apiService, @Nullable final Clock clock) {
+            @Nullable final SubscriptionApiService apiService, @Nullable final Clock clock) {
         super(builder.getId());
         this.apiService = apiService;
         this.clock = clock;
         this.bundleId = builder.getBundleId();
-        this.startDate = builder.getStartDate();
+        this.alignStartDate = builder.getAlignStartDate();
         this.bundleStartDate = builder.getBundleStartDate();
         this.category = builder.getCategory();
         this.activeVersion = builder.getActiveVersion();
@@ -108,7 +108,7 @@ public class SubscriptionData extends EntityBase implements Subscription {
 
     @Override
     public DateTime getStartDate() {
-        return startDate;
+        return transitions.get(0).getEffectiveTransitionTime();
     }
 
     @Override
@@ -118,6 +118,23 @@ public class SubscriptionData extends EntityBase implements Subscription {
     }
 
     @Override
+    public SubscriptionSourceType getSourceType() {
+        if (transitions == null) {
+            return null;
+        }
+        final SubscriptionTransitionData initialTransition = transitions.get(0);
+        switch (initialTransition.getApiEventType()) {
+        case MIGRATE_BILLING:
+        case MIGRATE_ENTITLEMENT:
+            return SubscriptionSourceType.MIGRATED;
+        case TRANSFER:
+            return SubscriptionSourceType.TRANSFERED;
+        default:
+            return SubscriptionSourceType.NATIVE;
+        }
+    }
+
+    @Override
     public PlanPhase getCurrentPhase() {
         return (getPreviousTransitionData() == null) ? null
                 : getPreviousTransitionData().getNextPhase();
@@ -132,7 +149,7 @@ public class SubscriptionData extends EntityBase implements Subscription {
     @Override
     public PriceList getCurrentPriceList() {
         return (getPreviousTransitionData() == null) ? null :
-                getPreviousTransitionData().getNextPriceList();
+            getPreviousTransitionData().getNextPriceList();
 
     }
 
@@ -165,31 +182,31 @@ public class SubscriptionData extends EntityBase implements Subscription {
 
     @Override
     public boolean cancel(final DateTime requestedDate, final boolean eot,
-                          final CallContext context) throws EntitlementUserApiException {
+            final CallContext context) throws EntitlementUserApiException {
         return apiService.cancel(this, requestedDate, eot, context);
     }
 
     @Override
     public boolean uncancel(final CallContext context)
-            throws EntitlementUserApiException {
+    throws EntitlementUserApiException {
         return apiService.uncancel(this, context);
     }
 
     @Override
     public boolean changePlan(final String productName, final BillingPeriod term, final String priceList,
-                              final DateTime requestedDate, final CallContext context) throws EntitlementUserApiException {
+            final DateTime requestedDate, final CallContext context) throws EntitlementUserApiException {
         return apiService.changePlan(this, productName, term, priceList, requestedDate, context);
     }
 
     @Override
     public boolean changePlanWithPolicy(final String productName, final BillingPeriod term, final String priceList,
-                                        final DateTime requestedDate, final ActionPolicy policy, final CallContext context) throws EntitlementUserApiException {
+            final DateTime requestedDate, final ActionPolicy policy, final CallContext context) throws EntitlementUserApiException {
         return apiService.changePlanWithPolicy(this, productName, term, priceList, requestedDate, policy, context);
     }
 
     @Override
     public boolean recreate(final PlanPhaseSpecifier spec, final DateTime requestedDate,
-                            final CallContext context) throws EntitlementUserApiException {
+            final CallContext context) throws EntitlementUserApiException {
         return apiService.recreatePlan(this, spec, requestedDate, context);
     }
 
@@ -199,7 +216,7 @@ public class SubscriptionData extends EntityBase implements Subscription {
         if (data == null) {
             return null;
         }
-        return new DefaultEffectiveSubscriptionEvent(data, startDate);
+        return new DefaultEffectiveSubscriptionEvent(data, alignStartDate);
     }
 
     @Override
@@ -223,7 +240,7 @@ public class SubscriptionData extends EntityBase implements Subscription {
         if (data == null) {
             return null;
         }
-        return new DefaultEffectiveSubscriptionEvent(data, startDate);
+        return new DefaultEffectiveSubscriptionEvent(data, alignStartDate);
     }
 
     protected SubscriptionTransitionData getPreviousTransitionData() {
@@ -260,7 +277,7 @@ public class SubscriptionData extends EntityBase implements Subscription {
         final int prime = 31;
         int result = 1;
         result = prime * result
-                + ((id == null) ? 0 : id.hashCode());
+        + ((id == null) ? 0 : id.hashCode());
         return result;
     }
 
@@ -297,7 +314,7 @@ public class SubscriptionData extends EntityBase implements Subscription {
                 clock, transitions, Order.ASC_FROM_PAST, Kind.BILLING,
                 Visibility.ALL, TimeLimit.ALL);
         while (it.hasNext()) {
-            result.add(new DefaultEffectiveSubscriptionEvent(it.next(), startDate));
+            result.add(new DefaultEffectiveSubscriptionEvent(it.next(), alignStartDate));
         }
         return result;
     }
@@ -309,12 +326,17 @@ public class SubscriptionData extends EntityBase implements Subscription {
         for (final SubscriptionTransitionData cur : transitions) {
             if (cur.getId().equals(event.getId())) {
                 final SubscriptionTransitionData withSeq = new SubscriptionTransitionData(cur, seqId);
-                return new DefaultEffectiveSubscriptionEvent(withSeq, startDate);
+                return new DefaultEffectiveSubscriptionEvent(withSeq, alignStartDate);
             }
         }
         return null;
     }
 
+    public DateTime getAlignStartDate() {
+        return alignStartDate;
+    }
+
+
     public long getLastEventOrderedId() {
         final SubscriptionTransitionDataIterator it = new SubscriptionTransitionDataIterator(
                 clock, transitions, Order.DESC_FROM_FUTURE, Kind.ENTITLEMENT,
@@ -335,7 +357,7 @@ public class SubscriptionData extends EntityBase implements Subscription {
         final List<EffectiveSubscriptionEvent> result = new ArrayList<EffectiveSubscriptionEvent>();
         final SubscriptionTransitionDataIterator it = new SubscriptionTransitionDataIterator(clock, transitions, Order.ASC_FROM_PAST, Kind.ALL, Visibility.ALL, TimeLimit.ALL);
         while (it.hasNext()) {
-            result.add(new DefaultEffectiveSubscriptionEvent(it.next(), startDate));
+            result.add(new DefaultEffectiveSubscriptionEvent(it.next(), alignStartDate));
         }
 
         return result;
@@ -347,16 +369,17 @@ public class SubscriptionData extends EntityBase implements Subscription {
         }
 
         final SubscriptionTransitionDataIterator it = new SubscriptionTransitionDataIterator(clock,
-                                                                                             transitions,
-                                                                                             Order.DESC_FROM_FUTURE,
-                                                                                             Kind.ENTITLEMENT,
-                                                                                             Visibility.ALL,
-                                                                                             TimeLimit.PAST_OR_PRESENT_ONLY);
+                transitions,
+                Order.DESC_FROM_FUTURE,
+                Kind.ENTITLEMENT,
+                Visibility.ALL,
+                TimeLimit.PAST_OR_PRESENT_ONLY);
 
         while (it.hasNext()) {
             final SubscriptionTransitionData cur = it.next();
             if (cur.getTransitionType() == SubscriptionTransitionType.CREATE
                     || cur.getTransitionType() == SubscriptionTransitionType.RE_CREATE
+                    || cur.getTransitionType() == SubscriptionTransitionType.TRANSFER
                     || cur.getTransitionType() == SubscriptionTransitionType.CHANGE
                     || cur.getTransitionType() == SubscriptionTransitionType.MIGRATE_ENTITLEMENT) {
                 return cur;
@@ -372,7 +395,7 @@ public class SubscriptionData extends EntityBase implements Subscription {
     }
 
     public DateTime getPlanChangeEffectiveDate(final ActionPolicy policy,
-                                               final DateTime requestedDate) {
+            final DateTime requestedDate) {
 
         if (policy == ActionPolicy.IMMEDIATE) {
             return requestedDate;
@@ -403,6 +426,7 @@ public class SubscriptionData extends EntityBase implements Subscription {
             final SubscriptionTransitionData cur = it.next();
 
             if (cur.getTransitionType() == SubscriptionTransitionType.PHASE
+                    || cur.getTransitionType() == SubscriptionTransitionType.TRANSFER
                     || cur.getTransitionType() == SubscriptionTransitionType.CREATE
                     || cur.getTransitionType() == SubscriptionTransitionType.RE_CREATE
                     || cur.getTransitionType() == SubscriptionTransitionType.CHANGE
@@ -415,7 +439,7 @@ public class SubscriptionData extends EntityBase implements Subscription {
     }
 
     public void rebuildTransitions(final List<EntitlementEvent> inputEvents,
-                                   final Catalog catalog) {
+            final Catalog catalog) {
 
         if (inputEvents == null) {
             return;
@@ -446,52 +470,53 @@ public class SubscriptionData extends EntityBase implements Subscription {
 
             switch (cur.getType()) {
 
-                case PHASE:
-                    final PhaseEvent phaseEV = (PhaseEvent) cur;
-                    nextPhaseName = phaseEV.getPhase();
+            case PHASE:
+                final PhaseEvent phaseEV = (PhaseEvent) cur;
+                nextPhaseName = phaseEV.getPhase();
+                break;
+
+            case API_USER:
+                final ApiEvent userEV = (ApiEvent) cur;
+                apiEventType = userEV.getEventType();
+                isFromDisk = userEV.isFromDisk();
+                nextUserToken = userEV.getUserToken();
+
+                switch (apiEventType) {
+                case TRANSFER:
+                case MIGRATE_BILLING:
+                case MIGRATE_ENTITLEMENT:
+                case CREATE:
+                case RE_CREATE:
+                    previousState = null;
+                    previousPlan = null;
+                    previousPhase = null;
+                    previousPriceList = null;
+                    nextState = SubscriptionState.ACTIVE;
+                    nextPlanName = userEV.getEventPlan();
+                    nextPhaseName = userEV.getEventPlanPhase();
+                    nextPriceListName = userEV.getPriceList();
                     break;
-
-                case API_USER:
-                    final ApiEvent userEV = (ApiEvent) cur;
-                    apiEventType = userEV.getEventType();
-                    isFromDisk = userEV.isFromDisk();
-                    nextUserToken = userEV.getUserToken();
-
-                    switch (apiEventType) {
-                        case MIGRATE_BILLING:
-                        case MIGRATE_ENTITLEMENT:
-                        case CREATE:
-                        case RE_CREATE:
-                            previousState = null;
-                            previousPlan = null;
-                            previousPhase = null;
-                            previousPriceList = null;
-                            nextState = SubscriptionState.ACTIVE;
-                            nextPlanName = userEV.getEventPlan();
-                            nextPhaseName = userEV.getEventPlanPhase();
-                            nextPriceListName = userEV.getPriceList();
-                            break;
-                        case CHANGE:
-                            nextPlanName = userEV.getEventPlan();
-                            nextPhaseName = userEV.getEventPlanPhase();
-                            nextPriceListName = userEV.getPriceList();
-                            break;
-                        case CANCEL:
-                            nextState = SubscriptionState.CANCELLED;
-                            nextPlanName = null;
-                            nextPhaseName = null;
-                            break;
-                        case UNCANCEL:
-                            break;
-                        default:
-                            throw new EntitlementError(String.format(
-                                    "Unexpected UserEvent type = %s", userEV
-                                    .getEventType().toString()));
-                    }
+                case CHANGE:
+                    nextPlanName = userEV.getEventPlan();
+                    nextPhaseName = userEV.getEventPlanPhase();
+                    nextPriceListName = userEV.getPriceList();
+                    break;
+                case CANCEL:
+                    nextState = SubscriptionState.CANCELLED;
+                    nextPlanName = null;
+                    nextPhaseName = null;
+                    break;
+                case UNCANCEL:
                     break;
                 default:
                     throw new EntitlementError(String.format(
-                            "Unexpected Event type = %s", cur.getType()));
+                            "Unexpected UserEvent type = %s", userEV
+                            .getEventType().toString()));
+                }
+                break;
+            default:
+                throw new EntitlementError(String.format(
+                        "Unexpected Event type = %s", cur.getType()));
             }
 
             Plan nextPlan = null;
@@ -499,8 +524,8 @@ public class SubscriptionData extends EntityBase implements Subscription {
             PriceList nextPriceList = null;
 
             try {
-                nextPlan = (nextPlanName != null) ? catalog.findPlan(nextPlanName, cur.getRequestedDate(), getStartDate()) : null;
-                nextPhase = (nextPhaseName != null) ? catalog.findPhase(nextPhaseName, cur.getRequestedDate(), getStartDate()) : null;
+                nextPlan = (nextPlanName != null) ? catalog.findPlan(nextPlanName, cur.getRequestedDate(), getAlignStartDate()) : null;
+                nextPhase = (nextPhaseName != null) ? catalog.findPhase(nextPhaseName, cur.getRequestedDate(), getAlignStartDate()) : null;
                 nextPriceList = (nextPriceListName != null) ? catalog.findPriceList(nextPriceListName, cur.getRequestedDate()) : null;
             } catch (CatalogApiException e) {
                 log.error(String.format("Failed to build transition for subscription %s", id), e);
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/AuditedEntitlementDao.java b/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/AuditedEntitlementDao.java
index e8c3d2e..b2816ce 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/AuditedEntitlementDao.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/AuditedEntitlementDao.java
@@ -50,6 +50,7 @@ import com.ning.billing.entitlement.api.migration.AccountMigrationData.Subscript
 import com.ning.billing.entitlement.api.timeline.DefaultRepairEntitlementEvent;
 import com.ning.billing.entitlement.api.timeline.RepairEntitlementEvent;
 import com.ning.billing.entitlement.api.timeline.SubscriptionDataRepair;
+import com.ning.billing.entitlement.api.transfer.TransferCancelData;
 import com.ning.billing.entitlement.api.user.DefaultRequestedSubscriptionEvent;
 import com.ning.billing.entitlement.api.user.DefaultSubscriptionFactory.SubscriptionBuilder;
 import com.ning.billing.entitlement.api.user.Subscription;
@@ -89,7 +90,6 @@ public class AuditedEntitlementDao implements EntitlementDao {
     private final NotificationQueueService notificationQueueService;
     private final AddonUtils addonUtils;
     private final Bus eventBus;
-    private final CatalogService catalogService;
 
     @Inject
     public AuditedEntitlementDao(final IDBI dbi, final Clock clock, final AddonUtils addonUtils,
@@ -101,12 +101,11 @@ public class AuditedEntitlementDao implements EntitlementDao {
         this.notificationQueueService = notificationQueueService;
         this.addonUtils = addonUtils;
         this.eventBus = eventBus;
-        this.catalogService = catalogService;
     }
 
     @Override
-    public SubscriptionBundle getSubscriptionBundleFromKey(final String bundleKey) {
-        return bundlesDao.getBundleFromKey(bundleKey);
+    public SubscriptionBundle getSubscriptionBundleFromAccountAndKey(final UUID accountId, final String bundleKey) {
+        return bundlesDao.getBundleFromAccountAndKey(accountId.toString(), bundleKey);
     }
 
     @Override
@@ -120,6 +119,12 @@ public class AuditedEntitlementDao implements EntitlementDao {
     }
 
     @Override
+    public List<SubscriptionBundle> getSubscriptionBundlesForKey(final String bundleKey) {
+        return bundlesDao.getBundlesForKey(bundleKey);
+    }
+
+
+    @Override
     public SubscriptionBundle createSubscriptionBundle(final SubscriptionBundleData bundle, final CallContext context) {
         return bundlesDao.inTransaction(new Transaction<SubscriptionBundle, BundleSqlDao>() {
             @Override
@@ -174,8 +179,8 @@ public class AuditedEntitlementDao implements EntitlementDao {
     }
 
     @Override
-    public List<Subscription> getSubscriptionsForKey(final SubscriptionFactory factory, final String bundleKey) {
-        final SubscriptionBundle bundle = bundlesDao.getBundleFromKey(bundleKey);
+    public List<Subscription> getSubscriptionsForAccountAndKey(final SubscriptionFactory factory, final UUID accountId, final String bundleKey) {
+        final SubscriptionBundle bundle = bundlesDao.getBundleFromAccountAndKey(accountId.toString(), bundleKey);
         if (bundle == null) {
             return Collections.emptyList();
         }
@@ -341,24 +346,7 @@ public class AuditedEntitlementDao implements EntitlementDao {
         eventsDao.inTransaction(new Transaction<Void, EntitlementEventSqlDao>() {
             @Override
             public Void inTransaction(final EntitlementEventSqlDao transactional, final TransactionStatus status) throws Exception {
-                final UUID subscriptionId = subscription.getId();
-                cancelNextCancelEventFromTransaction(subscriptionId, transactional, context);
-                cancelNextChangeEventFromTransaction(subscriptionId, transactional, context);
-                cancelNextPhaseEventFromTransaction(subscriptionId, transactional, context);
-                transactional.insertEvent(cancelEvent, context);
-                final String cancelEventId = cancelEvent.getId().toString();
-
-                final Long recordId = transactional.getRecordId(cancelEventId);
-                final EntityAudit audit = new EntityAudit(TableName.SUBSCRIPTION_EVENTS, recordId, ChangeType.INSERT);
-                transactional.insertAuditFromTransaction(audit, context);
-
-                recordFutureNotificationFromTransaction(transactional,
-                                                        cancelEvent.getEffectiveDate(),
-                                                        new EntitlementNotificationKey(cancelEvent.getId(), seqId));
-
-                // Notify the Bus of the requested change
-                notifyBusOfRequestedChange(transactional, subscription, cancelEvent);
-
+                cancelSubscriptionFromTransaction(subscription, cancelEvent, transactional, context, seqId);
                 return null;
             }
         });
@@ -442,6 +430,28 @@ public class AuditedEntitlementDao implements EntitlementDao {
         });
     }
 
+    private void cancelSubscriptionFromTransaction(final SubscriptionData subscription, final EntitlementEvent cancelEvent, final EntitlementEventSqlDao transactional, final CallContext context, final int seqId) {
+        final UUID subscriptionId = subscription.getId();
+        cancelNextCancelEventFromTransaction(subscriptionId, transactional, context);
+        cancelNextChangeEventFromTransaction(subscriptionId, transactional, context);
+        cancelNextPhaseEventFromTransaction(subscriptionId, transactional, context);
+        transactional.insertEvent(cancelEvent, context);
+        final String cancelEventId = cancelEvent.getId().toString();
+
+        final Long recordId = transactional.getRecordId(cancelEventId);
+        final EntityAudit audit = new EntityAudit(TableName.SUBSCRIPTION_EVENTS, recordId, ChangeType.INSERT);
+        transactional.insertAuditFromTransaction(audit, context);
+
+        recordFutureNotificationFromTransaction(transactional,
+                                                cancelEvent.getEffectiveDate(),
+                                                new EntitlementNotificationKey(cancelEvent.getId(), seqId));
+
+        // Notify the Bus of the requested change
+        notifyBusOfRequestedChange(transactional, subscription, cancelEvent);
+    }
+
+
+
     private void cancelNextPhaseEventFromTransaction(final UUID subscriptionId, final EntitlementEventSqlDao dao, final CallContext context) {
         cancelFutureEventFromTransaction(subscriptionId, dao, EventType.PHASE, null, context);
     }
@@ -529,7 +539,7 @@ public class AuditedEntitlementDao implements EntitlementDao {
                 } else if (o2.getCategory() == ProductCategory.BASE) {
                     return 1;
                 } else {
-                    return o1.getStartDate().compareTo(o2.getStartDate());
+                    return ((SubscriptionData) o1).getAlignStartDate().compareTo(((SubscriptionData) o2).getAlignStartDate());
                 }
             }
         });
@@ -594,44 +604,9 @@ public class AuditedEntitlementDao implements EntitlementDao {
         eventsDao.inTransaction(new Transaction<Void, EntitlementEventSqlDao>() {
             @Override
             public Void inTransaction(final EntitlementEventSqlDao transactional, final TransactionStatus status) throws Exception {
-                final SubscriptionSqlDao transSubDao = transactional.become(SubscriptionSqlDao.class);
-                final BundleSqlDao transBundleDao = transactional.become(BundleSqlDao.class);
-
-                final List<EntityAudit> audits = new ArrayList<EntityAudit>();
-
-                Long recordId;
-
                 for (final BundleMigrationData curBundle : accountData.getData()) {
-                    final SubscriptionBundleData bundleData = curBundle.getData();
-
-                    for (final SubscriptionMigrationData curSubscription : curBundle.getSubscriptions()) {
-                        final SubscriptionData subData = curSubscription.getData();
-                        for (final EntitlementEvent curEvent : curSubscription.getInitialEvents()) {
-                            transactional.insertEvent(curEvent, context);
-                            recordId = transactional.getRecordId(curEvent.getId().toString());
-                            audits.add(new EntityAudit(TableName.SUBSCRIPTION_EVENTS, recordId, ChangeType.INSERT));
-
-                            recordFutureNotificationFromTransaction(transactional,
-                                                                    curEvent.getEffectiveDate(),
-                                                                    new EntitlementNotificationKey(curEvent.getId()));
-                        }
-                        transSubDao.insertSubscription(subData, context);
-                        recordId = transSubDao.getRecordId(subData.getId().toString());
-                        audits.add(new EntityAudit(TableName.SUBSCRIPTIONS, recordId, ChangeType.INSERT));
-
-                        // Notify the Bus of the latest requested change
-                        final EntitlementEvent finalEvent = curSubscription.getInitialEvents().get(curSubscription.getInitialEvents().size() - 1);
-                        notifyBusOfRequestedChange(transactional, subData, finalEvent);
-                    }
-
-                    transBundleDao.insertBundle(bundleData, context);
-                    recordId = transBundleDao.getRecordId(bundleData.getId().toString());
-                    audits.add(new EntityAudit(TableName.BUNDLES, recordId, ChangeType.INSERT));
+                    migrateBundleDataFromTransaction(curBundle, transactional, context);
                 }
-
-                // add audit records for bundles, subscriptions, and events
-                transSubDao.insertAuditFromTransaction(audits, context);
-
                 return null;
             }
         });
@@ -644,7 +619,7 @@ public class AuditedEntitlementDao implements EntitlementDao {
             public Void inTransaction(final SubscriptionSqlDao transactional, final TransactionStatus status) throws Exception {
                 final EntitlementEventSqlDao transEventDao = transactional.become(EntitlementEventSqlDao.class);
                 for (final SubscriptionDataRepair cur : inRepair) {
-                    transactional.updateForRepair(cur.getId().toString(), cur.getActiveVersion(), cur.getStartDate().toDate(), cur.getBundleStartDate().toDate(), context);
+                    transactional.updateForRepair(cur.getId().toString(), cur.getActiveVersion(), cur.getAlignStartDate().toDate(), cur.getBundleStartDate().toDate(), context);
                     for (final EntitlementEvent event : cur.getInitialEvents()) {
                         transEventDao.updateVersion(event.getId().toString(), event.getActiveVersion(), context);
                     }
@@ -671,6 +646,26 @@ public class AuditedEntitlementDao implements EntitlementDao {
         });
     }
 
+    @Override
+    public void transfer(final UUID srcAccountId, final UUID destAccountId, final BundleMigrationData bundleTransferData,
+            final List<TransferCancelData> transferCancelData, final CallContext context) {
+
+        eventsDao.inTransaction(new Transaction<Void, EntitlementEventSqlDao>() {
+            @Override
+            public Void inTransaction(final EntitlementEventSqlDao transactional, final TransactionStatus status) throws Exception {
+
+                // Cancel the subscriptions for the old bundle
+                for (TransferCancelData cancel : transferCancelData) {
+                    cancelSubscriptionFromTransaction(cancel.getSubscription(), cancel.getCancelEvent(), transactional, context, 0);
+                }
+
+                migrateBundleDataFromTransaction(bundleTransferData, transactional, context);
+                return null;
+            }
+        });
+    }
+
+
     private Subscription getBaseSubscription(final SubscriptionFactory factory, final UUID bundleId, final boolean rebuildSubscription) {
         final List<Subscription> subscriptions = subscriptionsDao.getSubscriptionsFromBundleId(bundleId.toString());
         for (final Subscription cur : subscriptions) {
@@ -701,4 +696,40 @@ public class AuditedEntitlementDao implements EntitlementDao {
             log.warn("Failed to post requested change event for subscription " + subscription.getId(), e);
         }
     }
+
+    private void migrateBundleDataFromTransaction(final BundleMigrationData bundleTransferData, final EntitlementEventSqlDao transactional, final CallContext context) {
+
+        final SubscriptionSqlDao transSubDao = transactional.become(SubscriptionSqlDao.class);
+        final BundleSqlDao transBundleDao = transactional.become(BundleSqlDao.class);
+
+        final List<EntityAudit> audits = new ArrayList<EntityAudit>();
+        Long recordId;
+        final SubscriptionBundleData bundleData = bundleTransferData.getData();
+        for (final SubscriptionMigrationData curSubscription : bundleTransferData.getSubscriptions()) {
+            final SubscriptionData subData = curSubscription.getData();
+            for (final EntitlementEvent curEvent : curSubscription.getInitialEvents()) {
+                transactional.insertEvent(curEvent, context);
+                recordId = transactional.getRecordId(curEvent.getId().toString());
+                audits.add(new EntityAudit(TableName.SUBSCRIPTION_EVENTS, recordId, ChangeType.INSERT));
+
+                recordFutureNotificationFromTransaction(transactional,
+                        curEvent.getEffectiveDate(),
+                        new EntitlementNotificationKey(curEvent.getId()));
+            }
+            transSubDao.insertSubscription(subData, context);
+            recordId = transSubDao.getRecordId(subData.getId().toString());
+            audits.add(new EntityAudit(TableName.SUBSCRIPTIONS, recordId, ChangeType.INSERT));
+
+            // Notify the Bus of the latest requested change
+            final EntitlementEvent finalEvent = curSubscription.getInitialEvents().get(curSubscription.getInitialEvents().size() - 1);
+            notifyBusOfRequestedChange(transactional, subData, finalEvent);
+        }
+
+        transBundleDao.insertBundle(bundleData, context);
+        recordId = transBundleDao.getRecordId(bundleData.getId().toString());
+        audits.add(new EntityAudit(TableName.BUNDLES, recordId, ChangeType.INSERT));
+
+        // add audit records for bundles, subscriptions, and events
+        transSubDao.insertAuditFromTransaction(audits, context);
+    }
 }
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/BundleSqlDao.java b/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/BundleSqlDao.java
index 5c5e7fc..c48a5f2 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/BundleSqlDao.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/BundleSqlDao.java
@@ -60,11 +60,14 @@ public interface BundleSqlDao extends Transactional<BundleSqlDao>, EntitySqlDao<
     public SubscriptionBundle getBundleFromId(@Bind("id") String id);
 
     @SqlQuery
-    public SubscriptionBundle getBundleFromKey(@Bind("externalKey") String externalKey);
+    public SubscriptionBundle getBundleFromAccountAndKey(@Bind("accountId") String accountId, @Bind("externalKey") String externalKey);
 
     @SqlQuery
     public List<SubscriptionBundle> getBundleFromAccount(@Bind("accountId") String accountId);
 
+    @SqlQuery
+    public List<SubscriptionBundle> getBundlesForKey(@Bind("externalKey") String externalKey);
+
     public static class SubscriptionBundleBinder extends BinderBase implements Binder<Bind, SubscriptionBundleData> {
         @Override
         public void bind(@SuppressWarnings("rawtypes") final SQLStatement stmt, final Bind bind, final SubscriptionBundleData bundle) {
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/EntitlementDao.java b/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/EntitlementDao.java
index b85a47a..0544d1f 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/EntitlementDao.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/EntitlementDao.java
@@ -22,7 +22,9 @@ import java.util.UUID;
 
 import com.ning.billing.entitlement.api.SubscriptionFactory;
 import com.ning.billing.entitlement.api.migration.AccountMigrationData;
+import com.ning.billing.entitlement.api.migration.AccountMigrationData.BundleMigrationData;
 import com.ning.billing.entitlement.api.timeline.SubscriptionDataRepair;
+import com.ning.billing.entitlement.api.transfer.TransferCancelData;
 import com.ning.billing.entitlement.api.user.Subscription;
 import com.ning.billing.entitlement.api.user.SubscriptionBundle;
 import com.ning.billing.entitlement.api.user.SubscriptionBundleData;
@@ -34,7 +36,9 @@ public interface EntitlementDao {
     // Bundle apis
     public List<SubscriptionBundle> getSubscriptionBundleForAccount(final UUID accountId);
 
-    public SubscriptionBundle getSubscriptionBundleFromKey(final String bundleKey);
+    public List<SubscriptionBundle> getSubscriptionBundlesForKey(final String bundleKey);
+
+    public SubscriptionBundle getSubscriptionBundleFromAccountAndKey(final UUID accountId, final String bundleKey);
 
     public SubscriptionBundle getSubscriptionBundleFromId(final UUID bundleId);
 
@@ -50,7 +54,7 @@ public interface EntitlementDao {
 
     public List<Subscription> getSubscriptions(final SubscriptionFactory factory, final UUID bundleId);
 
-    public List<Subscription> getSubscriptionsForKey(final SubscriptionFactory factory, final String bundleKey);
+    public List<Subscription> getSubscriptionsForAccountAndKey(final SubscriptionFactory factory, final UUID accountId, final String bundleKey);
 
     // Update
     public void updateChargedThroughDate(final SubscriptionData subscription, final CallContext context);
@@ -79,6 +83,8 @@ public interface EntitlementDao {
 
     public void migrate(final UUID accountId, final AccountMigrationData data, final CallContext context);
 
+    public void transfer(final UUID srcAccountId, final UUID destAccountId, final BundleMigrationData data, final List<TransferCancelData> transferCancelData, final CallContext context);
+
     // Repair
     public void repair(final UUID accountId, final UUID bundleId, final List<SubscriptionDataRepair> inRepair, final CallContext context);
 }
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/EntitlementEventSqlDao.java b/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/EntitlementEventSqlDao.java
index 74973b7..4c89cf6 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/EntitlementEventSqlDao.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/EntitlementEventSqlDao.java
@@ -50,6 +50,7 @@ import com.ning.billing.entitlement.events.user.ApiEventCreate;
 import com.ning.billing.entitlement.events.user.ApiEventMigrateBilling;
 import com.ning.billing.entitlement.events.user.ApiEventMigrateEntitlement;
 import com.ning.billing.entitlement.events.user.ApiEventReCreate;
+import com.ning.billing.entitlement.events.user.ApiEventTransfer;
 import com.ning.billing.entitlement.events.user.ApiEventType;
 import com.ning.billing.entitlement.events.user.ApiEventUncancel;
 import com.ning.billing.entitlement.exceptions.EntitlementError;
@@ -176,6 +177,8 @@ public interface EntitlementEventSqlDao extends Transactional<EntitlementEventSq
                     result = new ApiEventMigrateEntitlement(builder);
                 } else if (userType == ApiEventType.MIGRATE_BILLING) {
                     result = new ApiEventMigrateBilling(builder);
+                } else if (userType == ApiEventType.TRANSFER) {
+                    result = new ApiEventTransfer(builder);
                 } else if (userType == ApiEventType.CHANGE) {
                     result = new ApiEventChange(builder);
                 } else if (userType == ApiEventType.CANCEL) {
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/RepairEntitlementDao.java b/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/RepairEntitlementDao.java
index c9f2681..ad82bd5 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/RepairEntitlementDao.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/RepairEntitlementDao.java
@@ -31,8 +31,10 @@ import com.google.common.base.Function;
 import com.google.common.collect.Collections2;
 import com.ning.billing.entitlement.api.SubscriptionFactory;
 import com.ning.billing.entitlement.api.migration.AccountMigrationData;
+import com.ning.billing.entitlement.api.migration.AccountMigrationData.BundleMigrationData;
 import com.ning.billing.entitlement.api.timeline.RepairEntitlementLifecycleDao;
 import com.ning.billing.entitlement.api.timeline.SubscriptionDataRepair;
+import com.ning.billing.entitlement.api.transfer.TransferCancelData;
 import com.ning.billing.entitlement.api.user.Subscription;
 import com.ning.billing.entitlement.api.user.SubscriptionBundle;
 import com.ning.billing.entitlement.api.user.SubscriptionBundleData;
@@ -213,7 +215,7 @@ public class RepairEntitlementDao implements EntitlementDao, RepairEntitlementLi
     }
 
     @Override
-    public SubscriptionBundle getSubscriptionBundleFromKey(final String bundleKey) {
+    public SubscriptionBundle getSubscriptionBundleFromAccountAndKey(final UUID accountId, final String bundleKey) {
         throw new EntitlementError(NOT_IMPLEMENTED);
     }
 
@@ -252,8 +254,7 @@ public class RepairEntitlementDao implements EntitlementDao, RepairEntitlementLi
     }
 
     @Override
-    public List<Subscription> getSubscriptionsForKey(
-            final SubscriptionFactory factory, final String bundleKey) {
+    public List<Subscription> getSubscriptionsForAccountAndKey(final SubscriptionFactory factory, final UUID accountId, final String bundleKey) {
         throw new EntitlementError(NOT_IMPLEMENTED);
     }
 
@@ -296,4 +297,17 @@ public class RepairEntitlementDao implements EntitlementDao, RepairEntitlementLi
                        final CallContext context) {
         throw new EntitlementError(NOT_IMPLEMENTED);
     }
+
+    @Override
+    public void transfer(UUID srcAccountId, UUID destAccountId,
+            BundleMigrationData data,
+            List<TransferCancelData> transferCancelData, CallContext context) {
+        throw new EntitlementError(NOT_IMPLEMENTED);
+    }
+
+    @Override
+    public List<SubscriptionBundle> getSubscriptionBundlesForKey(
+            String bundleKey) {
+        throw new EntitlementError(NOT_IMPLEMENTED);
+    }
 }
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/SubscriptionSqlDao.java b/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/SubscriptionSqlDao.java
index 861973c..5debc59 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/SubscriptionSqlDao.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/SubscriptionSqlDao.java
@@ -80,7 +80,7 @@ public interface SubscriptionSqlDao extends Transactional<SubscriptionSqlDao>, A
             stmt.bind("id", sub.getId().toString());
             stmt.bind("bundleId", sub.getBundleId().toString());
             stmt.bind("category", sub.getCategory().toString());
-            stmt.bind("startDate", getDate(sub.getStartDate()));
+            stmt.bind("startDate", getDate(sub.getAlignStartDate()));
             stmt.bind("bundleStartDate", getDate(sub.getBundleStartDate()));
             stmt.bind("activeVersion", sub.getActiveVersion());
             stmt.bind("chargedThroughDate", getDate(sub.getChargedThroughDate()));
@@ -107,7 +107,7 @@ public interface SubscriptionSqlDao extends Transactional<SubscriptionSqlDao>, A
                                                 .setBundleId(bundleId)
                                                 .setCategory(category)
                                                 .setBundleStartDate(bundleStartDate)
-                                                .setStartDate(startDate)
+                                                .setAlignStartDate(startDate)
                                                 .setActiveVersion(activeVersion)
                                                 .setChargedThroughDate(ctd)
                                                 .setPaidThroughDate(ptd));
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/events/user/ApiEventTransfer.java b/entitlement/src/main/java/com/ning/billing/entitlement/events/user/ApiEventTransfer.java
new file mode 100644
index 0000000..0773f34
--- /dev/null
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/events/user/ApiEventTransfer.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2010-2011 Ning, Inc.
+ *
+ * Ning 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 com.ning.billing.entitlement.events.user;
+
+public class ApiEventTransfer extends ApiEventBase {
+    public ApiEventTransfer(final ApiEventBuilder builder) {
+        super(builder.setEventType(ApiEventType.TRANSFER));
+    }
+
+}
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/events/user/ApiEventType.java b/entitlement/src/main/java/com/ning/billing/entitlement/events/user/ApiEventType.java
index fb1954c..1e8731f 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/events/user/ApiEventType.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/events/user/ApiEventType.java
@@ -38,6 +38,12 @@ public enum ApiEventType {
             return SubscriptionTransitionType.MIGRATE_BILLING;
         }
     },
+    TRANSFER {
+        @Override
+        public SubscriptionTransitionType getSubscriptionTransitionType() {
+            return SubscriptionTransitionType.TRANSFER;
+        }
+    },
     CHANGE {
         @Override
         public SubscriptionTransitionType getSubscriptionTransitionType() {
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/glue/DefaultEntitlementModule.java b/entitlement/src/main/java/com/ning/billing/entitlement/glue/DefaultEntitlementModule.java
index 5dc11bf..0d3fd20 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/glue/DefaultEntitlementModule.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/glue/DefaultEntitlementModule.java
@@ -35,6 +35,8 @@ import com.ning.billing.entitlement.api.timeline.EntitlementTimelineApi;
 import com.ning.billing.entitlement.api.timeline.RepairEntitlementLifecycleDao;
 import com.ning.billing.entitlement.api.timeline.RepairSubscriptionApiService;
 import com.ning.billing.entitlement.api.timeline.RepairSubscriptionFactory;
+import com.ning.billing.entitlement.api.transfer.DefaultEntitlementTransferApi;
+import com.ning.billing.entitlement.api.transfer.EntitlementTransferApi;
 import com.ning.billing.entitlement.api.user.DefaultEntitlementUserApi;
 import com.ning.billing.entitlement.api.user.DefaultSubscriptionApiService;
 import com.ning.billing.entitlement.api.user.DefaultSubscriptionFactory;
@@ -81,6 +83,7 @@ public class DefaultEntitlementModule extends AbstractModule implements Entitlem
         installEntitlementMigrationApi();
         installChargeThruApi();
         installEntitlementUserApi();
+        installEntitlementTransferApi();
     }
 
     @Override
@@ -105,6 +108,8 @@ public class DefaultEntitlementModule extends AbstractModule implements Entitlem
         bind(EntitlementMigrationApi.class).to(DefaultEntitlementMigrationApi.class).asEagerSingleton();
     }
 
+
+
     @Override
     public void installChargeThruApi() {
         bind(ChargeThruApi.class).to(DefaultChargeThruApi.class).asEagerSingleton();
@@ -114,4 +119,9 @@ public class DefaultEntitlementModule extends AbstractModule implements Entitlem
     public void installEntitlementUserApi() {
         bind(EntitlementUserApi.class).annotatedWith(RealImplementation.class).to(DefaultEntitlementUserApi.class).asEagerSingleton();
     }
+
+    @Override
+    public void installEntitlementTransferApi() {
+        bind(EntitlementTransferApi.class).to(DefaultEntitlementTransferApi.class).asEagerSingleton();
+    }
 }
diff --git a/entitlement/src/main/resources/com/ning/billing/entitlement/engine/dao/BundleSqlDao.sql.stg b/entitlement/src/main/resources/com/ning/billing/entitlement/engine/dao/BundleSqlDao.sql.stg
index 1ecf151..e4d5a68 100644
--- a/entitlement/src/main/resources/com/ning/billing/entitlement/engine/dao/BundleSqlDao.sql.stg
+++ b/entitlement/src/main/resources/com/ning/billing/entitlement/engine/dao/BundleSqlDao.sql.stg
@@ -28,7 +28,7 @@ getBundleFromId() ::= <<
     ;
 >>
 
-getBundleFromKey() ::= <<
+getBundlesForKey() ::= <<
     select <fields()>
     from bundles
     where
@@ -36,6 +36,14 @@ getBundleFromKey() ::= <<
     ;
 >>
 
+getBundleFromAccountAndKey() ::= <<
+    select <fields()>
+    from bundles
+    where
+      external_key = :externalKey AND account_id = :accountId
+    ;
+>>
+
 getBundleFromAccount() ::= <<
     select <fields()>
     from bundles
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/alignment/TestPlanAligner.java b/entitlement/src/test/java/com/ning/billing/entitlement/alignment/TestPlanAligner.java
index 65df594..aafeb08 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/alignment/TestPlanAligner.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/alignment/TestPlanAligner.java
@@ -166,11 +166,11 @@ public class TestPlanAligner extends KillbillTestSuite {
         final DefaultSubscriptionFactory.SubscriptionBuilder builder = new DefaultSubscriptionFactory.SubscriptionBuilder();
         builder.setBundleStartDate(clock.getUTCNow().minusHours(10));
         // Make sure to set the dates apart
-        builder.setStartDate(new DateTime(builder.getBundleStartDate().plusHours(5)));
+        builder.setAlignStartDate(new DateTime(builder.getBundleStartDate().plusHours(5)));
 
         // Create the transitions
         final SubscriptionData subscriptionData = new SubscriptionData(builder, null, clock);
-        final EntitlementEvent event = createEntitlementEvent(builder.getStartDate(),
+        final EntitlementEvent event = createEntitlementEvent(builder.getAlignStartDate(),
                                                               productName,
                                                               phaseType,
                                                               ApiEventType.CREATE,
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/api/TestApiBase.java b/entitlement/src/test/java/com/ning/billing/entitlement/api/TestApiBase.java
index 69ece03..88b24b1 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/api/TestApiBase.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/api/TestApiBase.java
@@ -57,6 +57,8 @@ import com.ning.billing.entitlement.EntitlementTestSuiteWithEmbeddedDB;
 import com.ning.billing.entitlement.api.billing.ChargeThruApi;
 import com.ning.billing.entitlement.api.migration.EntitlementMigrationApi;
 import com.ning.billing.entitlement.api.timeline.EntitlementTimelineApi;
+import com.ning.billing.entitlement.api.transfer.EntitlementTransferApi;
+import com.ning.billing.entitlement.api.transfer.EntitlementTransferApiException;
 import com.ning.billing.entitlement.api.user.EffectiveSubscriptionEvent;
 import com.ning.billing.entitlement.api.user.EntitlementUserApi;
 import com.ning.billing.entitlement.api.user.EntitlementUserApiException;
@@ -89,6 +91,7 @@ public abstract class TestApiBase extends EntitlementTestSuiteWithEmbeddedDB imp
     protected EntitlementService entitlementService;
     protected EntitlementUserApi entitlementApi;
     protected ChargeThruApi billingApi;
+    protected EntitlementTransferApi transferApi;
 
     protected EntitlementMigrationApi migrationApi;
     protected EntitlementTimelineApi repairApi;
@@ -160,6 +163,7 @@ public abstract class TestApiBase extends EntitlementTestSuiteWithEmbeddedDB imp
         billingApi = g.getInstance(ChargeThruApi.class);
         migrationApi = g.getInstance(EntitlementMigrationApi.class);
         repairApi = g.getInstance(EntitlementTimelineApi.class);
+        transferApi = g.getInstance(EntitlementTransferApi.class);
         catalogService = g.getInstance(CatalogService.class);
         busService = g.getInstance(BusService.class);
         config = g.getInstance(EntitlementConfig.class);
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/api/timeline/TestApiBaseRepair.java b/entitlement/src/test/java/com/ning/billing/entitlement/api/timeline/TestApiBaseRepair.java
index 449d1e7..6fb3525 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/api/timeline/TestApiBaseRepair.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/api/timeline/TestApiBaseRepair.java
@@ -1,4 +1,4 @@
-/* 
+/*
  * Copyright 2010-2011 Ning, Inc.
  *
  * Ning licenses this file to you under the Apache License, version 2.0
@@ -79,6 +79,11 @@ public abstract class TestApiBaseRepair extends TestApiBase {
             public List<DeletedEvent> getDeletedEvents() {
                 return deletedEvents;
             }
+
+            @Override
+            public long getActiveVersion() {
+                return 1;
+            }
         };
     }
 
@@ -135,6 +140,11 @@ public abstract class TestApiBaseRepair extends TestApiBase {
             public DateTime getEffectiveDate() {
                 return effectiveDateTime;
             }
+
+            @Override
+            public String getPlanPhaseName() {
+                return null;
+            }
         };
     }
 
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/api/timeline/TestRepairBP.java b/entitlement/src/test/java/com/ning/billing/entitlement/api/timeline/TestRepairBP.java
index e57df9b..0d73825 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/api/timeline/TestRepairBP.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/api/timeline/TestRepairBP.java
@@ -74,7 +74,7 @@ public class TestRepairBP extends TestApiBaseRepair {
 
         final SubscriptionData aoSubscription = createSubscription(aoProduct, aoTerm, aoPriceList);
 
-        final BundleTimeline bundleRepair = repairApi.getBundleRepair(bundle.getId());
+        final BundleTimeline bundleRepair = repairApi.getBundleTimeline(bundle.getId());
         final List<SubscriptionTimeline> subscriptionRepair = bundleRepair.getSubscriptions();
         assertEquals(subscriptionRepair.size(), 2);
 
@@ -134,7 +134,7 @@ public class TestRepairBP extends TestApiBaseRepair {
         final Interval it = new Interval(clock.getUTCNow(), clock.getUTCNow().plusDays(10));
         clock.addDeltaFromReality(it.toDurationMillis());
 
-        final BundleTimeline bundleRepair = repairApi.getBundleRepair(bundle.getId());
+        final BundleTimeline bundleRepair = repairApi.getBundleTimeline(bundle.getId());
         sortEventsOnBundle(bundleRepair);
 
         final List<DeletedEvent> des = new LinkedList<SubscriptionTimeline.DeletedEvent>();
@@ -306,7 +306,7 @@ public class TestRepairBP extends TestApiBaseRepair {
             }
         }
 
-        final BundleTimeline bundleRepair = repairApi.getBundleRepair(bundle.getId());
+        final BundleTimeline bundleRepair = repairApi.getBundleTimeline(bundle.getId());
         sortEventsOnBundle(bundleRepair);
 
         final DateTime newCreateTime = baseSubscription.getStartDate().plusDays(clockShift - 1);
@@ -476,7 +476,7 @@ public class TestRepairBP extends TestApiBaseRepair {
             assertTrue(testListener.isCompleted(5000));
         }
 
-        final BundleTimeline bundleRepair = repairApi.getBundleRepair(bundle.getId());
+        final BundleTimeline bundleRepair = repairApi.getBundleTimeline(bundle.getId());
         sortEventsOnBundle(bundleRepair);
 
         final DateTime changeTime = baseSubscription.getStartDate().plusDays(clockShift - 1);
@@ -597,7 +597,7 @@ public class TestRepairBP extends TestApiBaseRepair {
         assertEquals(currentPlan.getBillingPeriod(), BillingPeriod.MONTHLY);
 
         final DateTime repairTime = clock.getUTCNow().minusDays(1);
-        final BundleTimeline bundleRepair = repairApi.getBundleRepair(bundle.getId());
+        final BundleTimeline bundleRepair = repairApi.getBundleTimeline(bundle.getId());
         sortEventsOnBundle(bundleRepair);
 
         final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Assault-Rifle", ProductCategory.BASE, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, PhaseType.EVERGREEN);
@@ -647,7 +647,7 @@ public class TestRepairBP extends TestApiBaseRepair {
             @Override
             public void doTest() throws EntitlementRepairException, EntitlementUserApiException {
 
-                final BundleTimeline bundleRepair = repairApi.getBundleRepair(bundle.getId());
+                final BundleTimeline bundleRepair = repairApi.getBundleTimeline(bundle.getId());
                 sortEventsOnBundle(bundleRepair);
                 final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Assault-Rifle", ProductCategory.BASE, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, PhaseType.EVERGREEN);
                 final NewEvent ne = createNewEvent(SubscriptionTransitionType.CHANGE, baseSubscription.getStartDate().plusDays(10), spec);
@@ -680,7 +680,7 @@ public class TestRepairBP extends TestApiBaseRepair {
             @Override
             public void doTest() throws EntitlementRepairException, EntitlementUserApiException {
 
-                final BundleTimeline bundleRepair = repairApi.getBundleRepair(bundle.getId());
+                final BundleTimeline bundleRepair = repairApi.getBundleTimeline(bundle.getId());
                 sortEventsOnBundle(bundleRepair);
                 final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Assault-Rifle", ProductCategory.BASE, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, PhaseType.EVERGREEN);
                 final NewEvent ne = createNewEvent(SubscriptionTransitionType.CHANGE, baseSubscription.getStartDate().plusDays(10), spec);
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/api/timeline/TestRepairWithAO.java b/entitlement/src/test/java/com/ning/billing/entitlement/api/timeline/TestRepairWithAO.java
index 7757cec..661f960 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/api/timeline/TestRepairWithAO.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/api/timeline/TestRepairWithAO.java
@@ -75,7 +75,7 @@ public class TestRepairWithAO extends TestApiBaseRepair {
         it = new Interval(clock.getUTCNow(), clock.getUTCNow().plusDays(3));
         clock.addDeltaFromReality(it.toDurationMillis());
 
-        BundleTimeline bundleRepair = repairApi.getBundleRepair(bundle.getId());
+        BundleTimeline bundleRepair = repairApi.getBundleTimeline(bundle.getId());
         sortEventsOnBundle(bundleRepair);
 
         // Quick check
@@ -226,7 +226,7 @@ public class TestRepairWithAO extends TestApiBaseRepair {
         clock.addDeltaFromReality(it.toDurationMillis());
         assertTrue(testListener.isCompleted(7000));
 
-        BundleTimeline bundleRepair = repairApi.getBundleRepair(bundle.getId());
+        BundleTimeline bundleRepair = repairApi.getBundleTimeline(bundle.getId());
         sortEventsOnBundle(bundleRepair);
 
         // Quick check
@@ -350,7 +350,7 @@ public class TestRepairWithAO extends TestApiBaseRepair {
         billingApi.setChargedThroughDate(baseSubscription.getId(), newChargedThroughDate, context);
         baseSubscription = (SubscriptionData) entitlementApi.getSubscriptionFromId(baseSubscription.getId());
 
-        BundleTimeline bundleRepair = repairApi.getBundleRepair(bundle.getId());
+        BundleTimeline bundleRepair = repairApi.getBundleTimeline(bundle.getId());
         sortEventsOnBundle(bundleRepair);
 
         // Quick check
@@ -480,7 +480,7 @@ public class TestRepairWithAO extends TestApiBaseRepair {
         it = new Interval(clock.getUTCNow(), clock.getUTCNow().plusDays(3));
         clock.addDeltaFromReality(it.toDurationMillis());
 
-        final BundleTimeline bundleRepair = repairApi.getBundleRepair(bundle.getId());
+        final BundleTimeline bundleRepair = repairApi.getBundleTimeline(bundle.getId());
         sortEventsOnBundle(bundleRepair);
 
         // Quick check
@@ -570,7 +570,7 @@ public class TestRepairWithAO extends TestApiBaseRepair {
         it = new Interval(clock.getUTCNow(), clock.getUTCNow().plusDays(3));
         clock.addDeltaFromReality(it.toDurationMillis());
 
-        final BundleTimeline bundleRepair = repairApi.getBundleRepair(bundle.getId());
+        final BundleTimeline bundleRepair = repairApi.getBundleTimeline(bundle.getId());
         sortEventsOnBundle(bundleRepair);
 
         // Quick check
@@ -661,7 +661,7 @@ public class TestRepairWithAO extends TestApiBaseRepair {
         it = new Interval(clock.getUTCNow(), clock.getUTCNow().plusDays(3));
         clock.addDeltaFromReality(it.toDurationMillis());
 
-        final BundleTimeline bundleRepair = repairApi.getBundleRepair(bundle.getId());
+        final BundleTimeline bundleRepair = repairApi.getBundleTimeline(bundle.getId());
         sortEventsOnBundle(bundleRepair);
 
         // Quick check
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/api/timeline/TestRepairWithError.java b/entitlement/src/test/java/com/ning/billing/entitlement/api/timeline/TestRepairWithError.java
index b15446d..83d2f1e 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/api/timeline/TestRepairWithError.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/api/timeline/TestRepairWithError.java
@@ -78,7 +78,7 @@ public class TestRepairWithError extends TestApiBaseRepair {
 
                 assertTrue(testListener.isCompleted(5000));
 
-                final BundleTimeline bundleRepair = repairApi.getBundleRepair(bundle.getId());
+                final BundleTimeline bundleRepair = repairApi.getBundleTimeline(bundle.getId());
                 sortEventsOnBundle(bundleRepair);
                 final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Assault-Rifle", ProductCategory.BASE, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, PhaseType.EVERGREEN);
                 final NewEvent ne = createNewEvent(SubscriptionTransitionType.CHANGE, baseSubscription.getStartDate().plusDays(10), spec);
@@ -112,7 +112,7 @@ public class TestRepairWithError extends TestApiBaseRepair {
                 clock.addDeltaFromReality(it.toDurationMillis());
                 assertTrue(testListener.isCompleted(5000));
 
-                final BundleTimeline bundleRepair = repairApi.getBundleRepair(bundle.getId());
+                final BundleTimeline bundleRepair = repairApi.getBundleTimeline(bundle.getId());
                 sortEventsOnBundle(bundleRepair);
                 final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Assault-Rifle", ProductCategory.BASE, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, PhaseType.EVERGREEN);
                 final NewEvent ne = createNewEvent(SubscriptionTransitionType.CHANGE, baseSubscription.getStartDate().plusDays(10), spec);
@@ -132,7 +132,7 @@ public class TestRepairWithError extends TestApiBaseRepair {
             @Override
             public void doTest() throws EntitlementRepairException {
 
-                final BundleTimeline bundleRepair = repairApi.getBundleRepair(bundle.getId());
+                final BundleTimeline bundleRepair = repairApi.getBundleTimeline(bundle.getId());
                 sortEventsOnBundle(bundleRepair);
                 final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Assault-Rifle", ProductCategory.BASE, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, PhaseType.EVERGREEN);
                 final NewEvent ne = createNewEvent(SubscriptionTransitionType.CHANGE, baseSubscription.getStartDate().plusDays(10), spec);
@@ -158,7 +158,7 @@ public class TestRepairWithError extends TestApiBaseRepair {
                 clock.addDeltaFromReality(it.toDurationMillis());
                 assertTrue(testListener.isCompleted(5000));
 
-                final BundleTimeline bundleRepair = repairApi.getBundleRepair(bundle.getId());
+                final BundleTimeline bundleRepair = repairApi.getBundleTimeline(bundle.getId());
                 sortEventsOnBundle(bundleRepair);
                 final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Assault-Rifle", ProductCategory.BASE, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, PhaseType.EVERGREEN);
                 final NewEvent ne = createNewEvent(SubscriptionTransitionType.CREATE, baseSubscription.getStartDate().plusDays(10), spec);
@@ -187,7 +187,7 @@ public class TestRepairWithError extends TestApiBaseRepair {
                 clock.addDeltaFromReality(it.toDurationMillis());
                 assertTrue(testListener.isCompleted(5000));
 
-                final BundleTimeline bundleRepair = repairApi.getBundleRepair(bundle.getId());
+                final BundleTimeline bundleRepair = repairApi.getBundleTimeline(bundle.getId());
                 sortEventsOnBundle(bundleRepair);
                 final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Assault-Rifle", ProductCategory.BASE, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, PhaseType.EVERGREEN);
                 final NewEvent ne = createNewEvent(SubscriptionTransitionType.CHANGE, baseSubscription.getStartDate().plusDays(10), spec);
@@ -217,7 +217,7 @@ public class TestRepairWithError extends TestApiBaseRepair {
                 it = new Interval(clock.getUTCNow(), clock.getUTCNow().plusDays(4));
                 clock.addDeltaFromReality(it.toDurationMillis());
 
-                final BundleTimeline bundleRepair = repairApi.getBundleRepair(bundle.getId());
+                final BundleTimeline bundleRepair = repairApi.getBundleTimeline(bundle.getId());
                 sortEventsOnBundle(bundleRepair);
 
                 // Quick check
@@ -260,7 +260,7 @@ public class TestRepairWithError extends TestApiBaseRepair {
                 it = new Interval(clock.getUTCNow(), clock.getUTCNow().plusDays(4));
                 clock.addDeltaFromReality(it.toDurationMillis());
 
-                BundleTimeline bundleRepair = repairApi.getBundleRepair(bundle.getId());
+                BundleTimeline bundleRepair = repairApi.getBundleTimeline(bundle.getId());
                 sortEventsOnBundle(bundleRepair);
 
                 // Quick check
@@ -300,7 +300,7 @@ public class TestRepairWithError extends TestApiBaseRepair {
 
                 final SubscriptionData aoSubscription = createSubscription("Laser-Scope", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME);
 
-                final BundleTimeline bundleRepair = repairApi.getBundleRepair(bundle.getId());
+                final BundleTimeline bundleRepair = repairApi.getBundleTimeline(bundle.getId());
                 sortEventsOnBundle(bundleRepair);
 
                 final DateTime newCreateTime = baseSubscription.getStartDate().plusDays(3);
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/api/transfer/TestTransfer.java b/entitlement/src/test/java/com/ning/billing/entitlement/api/transfer/TestTransfer.java
new file mode 100644
index 0000000..be36ccb
--- /dev/null
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/api/transfer/TestTransfer.java
@@ -0,0 +1,366 @@
+/*
+ * Copyright 2010-2011 Ning, Inc.
+ *
+ * Ning 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 com.ning.billing.entitlement.api.transfer;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNotNull;
+import static org.testng.Assert.assertNull;
+import static org.testng.Assert.assertTrue;
+
+import java.util.List;
+import java.util.UUID;
+
+import org.joda.time.DateTime;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+import com.google.inject.Guice;
+import com.google.inject.Injector;
+import com.google.inject.Stage;
+import com.ning.billing.api.TestApiListener.NextEvent;
+import com.ning.billing.catalog.api.BillingPeriod;
+import com.ning.billing.catalog.api.PhaseType;
+import com.ning.billing.catalog.api.Plan;
+import com.ning.billing.catalog.api.PriceListSet;
+import com.ning.billing.catalog.api.Product;
+import com.ning.billing.entitlement.api.TestApiBase;
+import com.ning.billing.entitlement.api.user.Subscription;
+import com.ning.billing.entitlement.api.user.SubscriptionBundle;
+import com.ning.billing.entitlement.api.user.SubscriptionData;
+import com.ning.billing.entitlement.api.user.Subscription.SubscriptionState;
+import com.ning.billing.entitlement.glue.MockEngineModuleSql;
+
+public class TestTransfer extends TestApiBase {
+
+    protected static final Logger log = LoggerFactory.getLogger(TestTransfer.class);
+
+
+    @Override
+    protected Injector getInjector() {
+        return Guice.createInjector(Stage.DEVELOPMENT, new MockEngineModuleSql());
+    }
+
+    @Test(groups = "slow")
+    public void testTransferBPInTrialWithNoCTD() throws Exception {
+
+        final UUID newAccountId = UUID.randomUUID();
+
+        final String baseProduct = "Shotgun";
+        final BillingPeriod baseTerm = BillingPeriod.MONTHLY;
+        final String basePriceList = PriceListSet.DEFAULT_PRICELIST_NAME;
+
+        // CREATE BP
+        final Subscription baseSubscription = createSubscription(baseProduct, baseTerm, basePriceList);
+
+        final DateTime evergreenPhaseDate = baseSubscription.getPendingTransition().getEffectiveTransitionTime();
+
+        // MOVE A LITTLE, STILL IN TRIAL
+        clock.addDays(20);
+
+        final DateTime beforeTransferDate = clock.getUTCNow();
+        final DateTime transferRequestedDate = clock.getUTCNow();
+
+        testListener.pushExpectedEvent(NextEvent.TRANSFER);
+        testListener.pushExpectedEvent(NextEvent.CANCEL);
+        transferApi.transferBundle(bundle.getAccountId(), newAccountId, bundle.getKey(), transferRequestedDate, false, context);
+        assertTrue(testListener.isCompleted(3000));
+        final DateTime afterTransferDate = clock.getUTCNow();
+
+        // CHECK OLD BASE IS CANCEL AT THE TRANSFER DATE
+        final Subscription oldBaseSubscription = entitlementApi.getSubscriptionFromId(baseSubscription.getId());
+        assertNotNull(oldBaseSubscription.getEndDate());
+        assertDateWithin(oldBaseSubscription.getEndDate(), beforeTransferDate, afterTransferDate);
+        assertTrue(oldBaseSubscription.getEndDate().compareTo(transferRequestedDate) == 0);
+
+        // CHECK NEW BUNDLE EXIST, WITH ONE SUBSCRIPTION SARTING ON TRANSFER_DATE
+        SubscriptionBundle newBundle = entitlementApi.getBundleForAccountAndKey(newAccountId, bundle.getKey());
+
+        List<Subscription> subscriptions = entitlementApi.getSubscriptionsForBundle(newBundle.getId());
+        assertEquals(subscriptions.size(), 1);
+
+        final Subscription newBaseSubscription = subscriptions.get(0);
+        assertTrue(((SubscriptionData) newBaseSubscription).getAlignStartDate().compareTo(((SubscriptionData) oldBaseSubscription).getAlignStartDate()) == 0);
+
+        // CHECK NEXT PENDING PHASE IS ALIGNED WITH OLD SUBSCRIPTION START DATE
+        assertEquals(newBaseSubscription.getAllTransitions().size(), 2);
+        assertTrue(newBaseSubscription.getAllTransitions().get(1).getEffectiveTransitionTime().compareTo(evergreenPhaseDate) == 0);
+
+        Plan newPlan = newBaseSubscription.getCurrentPlan();
+        assertEquals(newPlan.getProduct().getName(), baseProduct);
+        assertEquals(newBaseSubscription.getCurrentPhase().getPhaseType(), PhaseType.TRIAL);
+    }
+
+
+    @Test(groups = "slow")
+    public void testTransferBPInTrialWithCTD() throws Exception {
+
+        final UUID newAccountId = UUID.randomUUID();
+
+        final String baseProduct = "Shotgun";
+        final BillingPeriod baseTerm = BillingPeriod.MONTHLY;
+        final String basePriceList = PriceListSet.DEFAULT_PRICELIST_NAME;
+
+        // CREATE BP
+        final Subscription baseSubscription = createSubscription(baseProduct, baseTerm, basePriceList);
+        final DateTime ctd = baseSubscription.getStartDate().plusDays(30);
+
+        billingApi.setChargedThroughDate(baseSubscription.getId(), ctd, context);
+
+        final DateTime evergreenPhaseDate = baseSubscription.getPendingTransition().getEffectiveTransitionTime();
+
+        // MOVE A LITTLE, STILL IN TRIAL
+        clock.addDays(20);
+
+        testListener.pushExpectedEvent(NextEvent.TRANSFER);
+        final DateTime transferRequestedDate = clock.getUTCNow();
+        transferApi.transferBundle(bundle.getAccountId(), newAccountId, bundle.getKey(), transferRequestedDate, false, context);
+        assertTrue(testListener.isCompleted(3000));
+
+        // CHECK OLD BASE IS CANCEL AT THE TRANSFER DATE
+        final Subscription oldBaseSubscription = entitlementApi.getSubscriptionFromId(baseSubscription.getId());
+        assertNotNull(oldBaseSubscription.getFutureEndDate());
+        assertTrue(oldBaseSubscription.getFutureEndDate().compareTo(ctd) == 0);
+
+        // CHECK NEW BUNDLE EXIST, WITH ONE SUBSCRIPTION SARTING ON TRANSFER_DATE
+        SubscriptionBundle newBundle = entitlementApi.getBundleForAccountAndKey(newAccountId, bundle.getKey());
+
+        List<Subscription> subscriptions = entitlementApi.getSubscriptionsForBundle(newBundle.getId());
+        assertEquals(subscriptions.size(), 1);
+
+        final Subscription newBaseSubscription = subscriptions.get(0);
+        assertTrue(((SubscriptionData) newBaseSubscription).getAlignStartDate().compareTo(((SubscriptionData) oldBaseSubscription).getAlignStartDate()) == 0);
+
+        // CHECK NEXT PENDING PHASE IS ALIGNED WITH OLD SUBSCRIPTION START DATE
+        assertEquals(newBaseSubscription.getAllTransitions().size(), 2);
+        assertTrue(newBaseSubscription.getAllTransitions().get(1).getEffectiveTransitionTime().compareTo(evergreenPhaseDate) == 0);
+
+        Plan newPlan = newBaseSubscription.getCurrentPlan();
+        assertEquals(newPlan.getProduct().getName(), baseProduct);
+        assertEquals(newBaseSubscription.getCurrentPhase().getPhaseType(), PhaseType.TRIAL);
+    }
+
+
+    @Test(groups = "slow")
+    public void testTransferBPNoTrialWithNoCTD() throws Exception {
+
+        final UUID newAccountId = UUID.randomUUID();
+
+        final String baseProduct = "Shotgun";
+        final BillingPeriod baseTerm = BillingPeriod.MONTHLY;
+        final String basePriceList = PriceListSet.DEFAULT_PRICELIST_NAME;
+
+        // CREATE BP
+        final Subscription baseSubscription = createSubscription(baseProduct, baseTerm, basePriceList);
+
+        // MOVE AFTER TRIAL
+        testListener.pushExpectedEvent(NextEvent.PHASE);
+        clock.addDays(40);
+        assertTrue(testListener.isCompleted(3000));
+
+        final DateTime beforeTransferDate = clock.getUTCNow();
+        final DateTime transferRequestedDate = clock.getUTCNow();
+        testListener.pushExpectedEvent(NextEvent.TRANSFER);
+        testListener.pushExpectedEvent(NextEvent.CANCEL);
+        transferApi.transferBundle(bundle.getAccountId(), newAccountId, bundle.getKey(), transferRequestedDate, false, context);
+        assertTrue(testListener.isCompleted(3000));
+        final DateTime afterTransferDate = clock.getUTCNow();
+
+        // CHECK OLD BASE IS CANCEL AT THE TRANSFER DATE
+        final Subscription oldBaseSubscription = entitlementApi.getSubscriptionFromId(baseSubscription.getId());
+        assertNotNull(oldBaseSubscription.getEndDate());
+        assertDateWithin(oldBaseSubscription.getEndDate(), beforeTransferDate, afterTransferDate);
+        assertTrue(oldBaseSubscription.getEndDate().compareTo(transferRequestedDate) == 0);
+
+        // CHECK NEW BUNDLE EXIST, WITH ONE SUBSCRIPTION SARTING ON TRANSFER_DATE
+        SubscriptionBundle newBundle = entitlementApi.getBundleForAccountAndKey(newAccountId, bundle.getKey());
+
+        List<Subscription> subscriptions = entitlementApi.getSubscriptionsForBundle(newBundle.getId());
+        assertEquals(subscriptions.size(), 1);
+
+        final Subscription newBaseSubscription = subscriptions.get(0);
+        assertTrue(((SubscriptionData) newBaseSubscription).getAlignStartDate().compareTo(((SubscriptionData) baseSubscription).getAlignStartDate()) == 0);
+
+        // CHECK ONLY ONE PHASE EXISTS
+        assertEquals(newBaseSubscription.getAllTransitions().size(), 1);
+
+        Plan newPlan = newBaseSubscription.getCurrentPlan();
+        assertEquals(newPlan.getProduct().getName(), baseProduct);
+        assertEquals(newBaseSubscription.getCurrentPhase().getPhaseType(), PhaseType.EVERGREEN);
+    }
+
+    @Test(groups = "slow")
+    public void testTransferBPNoTrialWithCTD() throws Exception {
+
+        final UUID newAccountId = UUID.randomUUID();
+
+        final String baseProduct = "Shotgun";
+        final BillingPeriod baseTerm = BillingPeriod.MONTHLY;
+        final String basePriceList = PriceListSet.DEFAULT_PRICELIST_NAME;
+
+        // CREATE BP
+        final Subscription baseSubscription = createSubscription(baseProduct, baseTerm, basePriceList);
+
+        // MOVE AFTER TRIAL
+        testListener.pushExpectedEvent(NextEvent.PHASE);
+        clock.addDays(40);
+        assertTrue(testListener.isCompleted(3000));
+
+        // SET CTD
+        final DateTime ctd = baseSubscription.getStartDate().plusDays(30).plusMonths(1);
+        billingApi.setChargedThroughDate(baseSubscription.getId(), ctd, context);
+
+
+        final DateTime transferRequestedDate = clock.getUTCNow();
+        testListener.pushExpectedEvent(NextEvent.TRANSFER);
+        transferApi.transferBundle(bundle.getAccountId(), newAccountId, bundle.getKey(), transferRequestedDate, false, context);
+        assertTrue(testListener.isCompleted(3000));
+
+        // CHECK OLD BASE IS CANCEL AT THE TRANSFER DATE
+        final Subscription oldBaseSubscription = entitlementApi.getSubscriptionFromId(baseSubscription.getId());
+        assertNotNull(oldBaseSubscription.getFutureEndDate());
+        assertTrue(oldBaseSubscription.getFutureEndDate().compareTo(ctd) == 0);
+
+        // CHECK NEW BUNDLE EXIST, WITH ONE SUBSCRIPTION SARTING ON TRANSFER_DATE
+        SubscriptionBundle newBundle = entitlementApi.getBundleForAccountAndKey(newAccountId, bundle.getKey());
+
+        List<Subscription> subscriptions = entitlementApi.getSubscriptionsForBundle(newBundle.getId());
+        assertEquals(subscriptions.size(), 1);
+
+        final Subscription newBaseSubscription = subscriptions.get(0);
+        assertTrue(((SubscriptionData) newBaseSubscription).getAlignStartDate().compareTo(((SubscriptionData) baseSubscription).getAlignStartDate()) == 0);
+
+        // CHECK ONLY ONE PHASE EXISTS
+        assertEquals(newBaseSubscription.getAllTransitions().size(), 1);
+
+        Plan newPlan = newBaseSubscription.getCurrentPlan();
+        assertEquals(newPlan.getProduct().getName(), baseProduct);
+        assertEquals(newBaseSubscription.getCurrentPhase().getPhaseType(), PhaseType.EVERGREEN);
+
+        // MAKE A PLAN CHANGE IMM
+        clock.addDays(5);
+
+        final String newBaseProduct1 = "Assault-Rifle";
+        final BillingPeriod newBaseTerm1 = BillingPeriod.ANNUAL;
+        final DateTime changeDate1 = clock.getUTCNow();
+        newBaseSubscription.changePlan(newBaseProduct1, newBaseTerm1, basePriceList, changeDate1, context);
+
+        newPlan = newBaseSubscription.getCurrentPlan();
+        assertEquals(newPlan.getProduct().getName(), newBaseProduct1);
+        assertEquals(newBaseSubscription.getCurrentPhase().getPhaseType(), PhaseType.EVERGREEN);
+
+        // SET CTD AND MAKE CHANGE EOT
+        clock.addDays(2);
+
+        DateTime newCtd = newBaseSubscription.getStartDate().plusYears(1);
+        billingApi.setChargedThroughDate(newBaseSubscription.getId(), newCtd, context);
+        final Subscription newBaseSubscriptionWithCtd = entitlementApi.getSubscriptionFromId(newBaseSubscription.getId());
+
+        final String newBaseProduct2 = "Pistol";
+        final BillingPeriod newBaseTerm2 = BillingPeriod.ANNUAL;
+        final DateTime changeDate2 = clock.getUTCNow();
+        newBaseSubscriptionWithCtd.changePlan(newBaseProduct2, newBaseTerm2, basePriceList, changeDate2, context);
+
+        newPlan = newBaseSubscriptionWithCtd.getCurrentPlan();
+        assertEquals(newPlan.getProduct().getName(), newBaseProduct1);
+        assertEquals(newBaseSubscriptionWithCtd.getCurrentPhase().getPhaseType(), PhaseType.EVERGREEN);
+
+        assertNotNull(newBaseSubscriptionWithCtd.getPendingTransition());
+        assertEquals(newBaseSubscriptionWithCtd.getPendingTransition().getEffectiveTransitionTime(), newCtd);
+    }
+
+    @Test(groups = "slow")
+    public void testTransferWithAO() throws Exception {
+
+        final UUID newAccountId = UUID.randomUUID();
+
+        final String baseProduct = "Shotgun";
+        final BillingPeriod baseTerm = BillingPeriod.MONTHLY;
+        final String basePriceList = PriceListSet.DEFAULT_PRICELIST_NAME;
+
+        // CREATE BP
+        final Subscription baseSubscription = createSubscription(baseProduct, baseTerm, basePriceList);
+
+        // MOVE 3 DAYS AND CREATE AO1
+        clock.addDays(3);
+        final String aoProduct1 = "Telescopic-Scope";
+        final BillingPeriod aoTerm1 = BillingPeriod.MONTHLY;
+        final SubscriptionData aoSubscription1 = createSubscription(aoProduct1, aoTerm1, basePriceList);
+        assertEquals(aoSubscription1.getState(), SubscriptionState.ACTIVE);
+
+        // MOVE ANOTHER 25 DAYS AND CREATE AO2 [ BP STILL IN TRIAL]
+        // LASER-SCOPE IS SUBSCRIPTION ALIGN SO EVERGREN WILL ONLY START IN A MONTH
+        clock.addDays(25);
+        final String aoProduct2 = "Laser-Scope";
+        final BillingPeriod aoTerm2 = BillingPeriod.MONTHLY;
+        final SubscriptionData aoSubscription2 = createSubscription(aoProduct2, aoTerm2, basePriceList);
+        assertEquals(aoSubscription2.getState(), SubscriptionState.ACTIVE);
+
+        // MOVE AFTER TRIAL AND AO DISCOUNT PHASE [LASER SCOPE STILL IN DISCOUNT]
+        testListener.pushExpectedEvent(NextEvent.PHASE);
+        testListener.pushExpectedEvent(NextEvent.PHASE);
+        clock.addDays(5);
+        assertTrue(testListener.isCompleted(3000));
+
+        // SET CTD TO TRIGGER CANCELLATION EOT
+        final DateTime ctd = baseSubscription.getStartDate().plusDays(30).plusMonths(1);
+        billingApi.setChargedThroughDate(baseSubscription.getId(), ctd, context);
+
+        final DateTime transferRequestedDate = clock.getUTCNow();
+        testListener.pushExpectedEvent(NextEvent.TRANSFER);
+        transferApi.transferBundle(bundle.getAccountId(), newAccountId, bundle.getKey(), transferRequestedDate, true, context);
+        assertTrue(testListener.isCompleted(3000));
+
+        // RETRIEVE NEW BUNDLE AND CHECK SUBSCRIPTIONS
+        SubscriptionBundle newBundle = entitlementApi.getBundleForAccountAndKey(newAccountId, bundle.getKey());
+        List<Subscription> subscriptions = entitlementApi.getSubscriptionsForBundle(newBundle.getId());
+        assertEquals(subscriptions.size(), 3);
+        boolean foundBP = false;
+        boolean foundAO1 = false;
+        boolean foundAO2 = false;
+        for (Subscription cur : subscriptions) {
+            Plan curPlan = cur.getCurrentPlan();
+            Product curProduct = curPlan.getProduct();
+            if (curProduct.getName().equals(baseProduct)) {
+                foundBP = true;
+                assertTrue(((SubscriptionData) cur).getAlignStartDate().compareTo(((SubscriptionData) baseSubscription).getAlignStartDate()) == 0);
+                assertNull(cur.getPendingTransition());
+            } else if (curProduct.getName().equals(aoProduct1)) {
+                foundAO1 = true;
+                assertTrue(((SubscriptionData) cur).getAlignStartDate().compareTo((aoSubscription1).getAlignStartDate()) == 0);
+                assertNull(cur.getPendingTransition());
+            } else if (curProduct.getName().equals(aoProduct2)) {
+                foundAO2 = true;
+                assertTrue(((SubscriptionData) cur).getAlignStartDate().compareTo((aoSubscription2).getAlignStartDate()) == 0);
+                assertNotNull(cur.getPendingTransition());
+            } else {
+                Assert.fail("Unexpected product " + curProduct.getName());
+            }
+        }
+        assertTrue(foundBP);
+        assertTrue(foundAO1);
+        assertTrue(foundAO2);
+
+        // MOVE AFTER CANCEL DATE TO TRIGGER OLD SUBSCRIPTIONS CANCELLATION + LASER_SCOPE PHASE EVENT
+        testListener.pushExpectedEvent(NextEvent.PHASE);
+        testListener.pushExpectedEvent(NextEvent.CANCEL);
+        testListener.pushExpectedEvent(NextEvent.CANCEL);
+        testListener.pushExpectedEvent(NextEvent.CANCEL);
+        clock.addMonths(1);
+        assertTrue(testListener.isCompleted(3000));
+    }
+}
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/engine/dao/MockEntitlementDaoMemory.java b/entitlement/src/test/java/com/ning/billing/entitlement/engine/dao/MockEntitlementDaoMemory.java
index 3479d0e..8463775 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/engine/dao/MockEntitlementDaoMemory.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/engine/dao/MockEntitlementDaoMemory.java
@@ -40,6 +40,7 @@ import com.ning.billing.entitlement.api.migration.AccountMigrationData;
 import com.ning.billing.entitlement.api.migration.AccountMigrationData.BundleMigrationData;
 import com.ning.billing.entitlement.api.migration.AccountMigrationData.SubscriptionMigrationData;
 import com.ning.billing.entitlement.api.timeline.SubscriptionDataRepair;
+import com.ning.billing.entitlement.api.transfer.TransferCancelData;
 import com.ning.billing.entitlement.api.user.DefaultSubscriptionFactory.SubscriptionBuilder;
 import com.ning.billing.entitlement.api.user.Subscription;
 import com.ning.billing.entitlement.api.user.SubscriptionBundle;
@@ -58,6 +59,7 @@ import com.ning.billing.util.notificationq.NotificationQueue;
 import com.ning.billing.util.notificationq.NotificationQueueService;
 import com.ning.billing.util.notificationq.NotificationQueueService.NoSuchNotificationQueue;
 
+
 public class MockEntitlementDaoMemory implements EntitlementDao {
     protected static final Logger log = LoggerFactory.getLogger(EntitlementDao.class);
 
@@ -99,6 +101,18 @@ public class MockEntitlementDaoMemory implements EntitlementDao {
     }
 
     @Override
+    public List<SubscriptionBundle> getSubscriptionBundlesForKey(final String bundleKey) {
+        final List<SubscriptionBundle> results = new ArrayList<SubscriptionBundle>();
+        for (final SubscriptionBundle cur : bundles) {
+            if (cur.getKey().equals(bundleKey)) {
+                results.add(cur);
+            }
+        }
+        return results;
+    }
+
+
+    @Override
     public SubscriptionBundle getSubscriptionBundleFromId(final UUID bundleId) {
         for (final SubscriptionBundle cur : bundles) {
             if (cur.getId().equals(bundleId)) {
@@ -109,9 +123,9 @@ public class MockEntitlementDaoMemory implements EntitlementDao {
     }
 
     @Override
-    public SubscriptionBundle getSubscriptionBundleFromKey(final String bundleKey) {
+    public SubscriptionBundle getSubscriptionBundleFromAccountAndKey(final UUID accountId, final String bundleKey) {
         for (final SubscriptionBundle cur : bundles) {
-            if (cur.getKey().equals(bundleKey)) {
+            if (cur.getKey().equals(bundleKey) && cur.getAccountId().equals(accountId)) {
                 return cur;
             }
         }
@@ -140,10 +154,10 @@ public class MockEntitlementDaoMemory implements EntitlementDao {
     }
 
     @Override
-    public List<Subscription> getSubscriptionsForKey(final SubscriptionFactory factory, final String bundleKey) {
+    public List<Subscription> getSubscriptionsForAccountAndKey(final SubscriptionFactory factory, final UUID accountId, final String bundleKey) {
 
         for (final SubscriptionBundle cur : bundles) {
-            if (cur.getKey().equals(bundleKey)) {
+            if (cur.getKey().equals(bundleKey) && cur.getAccountId().equals(bundleKey)) {
                 return getSubscriptions(factory, cur.getId());
             }
         }
@@ -418,4 +432,10 @@ public class MockEntitlementDaoMemory implements EntitlementDao {
     public void repair(final UUID accountId, final UUID bundleId, final List<SubscriptionDataRepair> inRepair,
                        final CallContext context) {
     }
+
+    @Override
+    public void transfer(UUID srcAccountId, UUID destAccountId,
+            BundleMigrationData data,
+            List<TransferCancelData> transferCancelData, CallContext context) {
+    }
 }
diff --git a/entitlement/src/test/resources/entitlement.properties b/entitlement/src/test/resources/entitlement.properties
index 58f4f95..7039cc2 100644
--- a/entitlement/src/test/resources/entitlement.properties
+++ b/entitlement/src/test/resources/entitlement.properties
@@ -5,4 +5,3 @@ killbill.entitlement.engine.notifications.sleep=100
 killbill.billing.util.persistent.bus.sleep=100
 killbill.billing.util.persistent.bus.nbThreads=1
 user.timezone=UTC
-
diff --git a/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/AccountResource.java b/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/AccountResource.java
index 25eaf57..20d0320 100644
--- a/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/AccountResource.java
+++ b/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/AccountResource.java
@@ -47,6 +47,7 @@ import com.ning.billing.entitlement.api.timeline.BundleTimeline;
 import com.ning.billing.entitlement.api.timeline.EntitlementRepairException;
 import com.ning.billing.entitlement.api.timeline.EntitlementTimelineApi;
 import com.ning.billing.entitlement.api.user.EntitlementUserApi;
+import com.ning.billing.entitlement.api.user.EntitlementUserApiException;
 import com.ning.billing.entitlement.api.user.SubscriptionBundle;
 import com.ning.billing.invoice.api.Invoice;
 import com.ning.billing.invoice.api.InvoicePayment;
@@ -131,18 +132,26 @@ public class AccountResource extends JaxRsResourceBase {
     @GET
     @Path("/{accountId:" + UUID_PATTERN + "}/" + BUNDLES)
     @Produces(APPLICATION_JSON)
-    public Response getAccountBundles(@PathParam("accountId") final String accountId) throws AccountApiException {
+    public Response getAccountBundles(@PathParam("accountId") final String accountId, @QueryParam(QUERY_EXTERNAL_KEY) final String externalKey)
+        throws AccountApiException, EntitlementUserApiException {
+
         final UUID uuid = UUID.fromString(accountId);
         accountApi.getAccountById(uuid);
 
-        final List<SubscriptionBundle> bundles = entitlementApi.getBundlesForAccount(uuid);
-        final Collection<BundleJsonNoSubscriptions> result = Collections2.transform(bundles, new Function<SubscriptionBundle, BundleJsonNoSubscriptions>() {
-            @Override
-            public BundleJsonNoSubscriptions apply(final SubscriptionBundle input) {
-                return new BundleJsonNoSubscriptions(input);
-            }
-        });
-        return Response.status(Status.OK).entity(result).build();
+        if (externalKey != null) {
+            final SubscriptionBundle bundle = entitlementApi.getBundleForAccountAndKey(uuid, externalKey);
+            final BundleJsonNoSubscriptions json = new BundleJsonNoSubscriptions(bundle);
+            return Response.status(Status.OK).entity(json).build();
+        } else {
+            final List<SubscriptionBundle> bundles = entitlementApi.getBundlesForAccount(uuid);
+            final Collection<BundleJsonNoSubscriptions> result = Collections2.transform(bundles, new Function<SubscriptionBundle, BundleJsonNoSubscriptions>() {
+                @Override
+                public BundleJsonNoSubscriptions apply(final SubscriptionBundle input) {
+                    return new BundleJsonNoSubscriptions(input);
+                }
+            });
+            return Response.status(Status.OK).entity(result).build();
+        }
     }
 
     @GET
@@ -229,7 +238,7 @@ public class AccountResource extends JaxRsResourceBase {
         final List<SubscriptionBundle> bundles = entitlementApi.getBundlesForAccount(account.getId());
         final List<BundleTimeline> bundlesTimeline = new LinkedList<BundleTimeline>();
         for (final SubscriptionBundle cur : bundles) {
-            bundlesTimeline.add(timelineApi.getBundleRepair(cur.getId()));
+            bundlesTimeline.add(timelineApi.getBundleTimeline(cur.getId()));
         }
 
         final AccountTimelineJson json = new AccountTimelineJson(account, invoices, payments, bundlesTimeline,
@@ -238,6 +247,10 @@ public class AccountResource extends JaxRsResourceBase {
         return Response.status(Status.OK).entity(json).build();
     }
 
+
+
+
+
     /*
      * ************************** EMAIL NOTIFICATIONS FOR INVOICES ********************************
      */
diff --git a/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/BundleResource.java b/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/BundleResource.java
index 44e5e77..b1c3586 100644
--- a/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/BundleResource.java
+++ b/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/BundleResource.java
@@ -86,13 +86,6 @@ public class BundleResource extends JaxRsResourceBase {
         return Response.status(Status.OK).entity(json).build();
     }
 
-    @GET
-    @Produces(APPLICATION_JSON)
-    public Response getBundleByKey(@QueryParam(QUERY_EXTERNAL_KEY) final String externalKey) throws EntitlementUserApiException {
-        final SubscriptionBundle bundle = entitlementApi.getBundleForKey(externalKey);
-        final BundleJsonNoSubscriptions json = new BundleJsonNoSubscriptions(bundle);
-        return Response.status(Status.OK).entity(json).build();
-    }
 
     @POST
     @Consumes(APPLICATION_JSON)
diff --git a/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/JaxrsResource.java b/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/JaxrsResource.java
index 7e22ed1..932d1e2 100644
--- a/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/JaxrsResource.java
+++ b/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/JaxrsResource.java
@@ -41,7 +41,7 @@ public interface JaxrsResource {
     /*
      * Query parameters
      */
-    public static final String QUERY_EXTERNAL_KEY = "external_key";
+    public static final String QUERY_EXTERNAL_KEY = "externalKey";
     public static final String QUERY_REQUESTED_DT = "requestedDate";
     public static final String QUERY_CALL_COMPLETION = "callCompletion";
     public static final String QUERY_CALL_TIMEOUT = "callTimeoutSec";
diff --git a/junction/src/main/java/com/ning/billing/junction/plumbing/api/BlockingEntitlementUserApi.java b/junction/src/main/java/com/ning/billing/junction/plumbing/api/BlockingEntitlementUserApi.java
index a2bd036..d89c64d 100644
--- a/junction/src/main/java/com/ning/billing/junction/plumbing/api/BlockingEntitlementUserApi.java
+++ b/junction/src/main/java/com/ning/billing/junction/plumbing/api/BlockingEntitlementUserApi.java
@@ -64,8 +64,8 @@ public class BlockingEntitlementUserApi implements EntitlementUserApi {
     }
 
     @Override
-    public SubscriptionBundle getBundleForKey(final String bundleKey) throws EntitlementUserApiException {
-        final SubscriptionBundle bundle = entitlementUserApi.getBundleForKey(bundleKey);
+    public SubscriptionBundle getBundleForAccountAndKey(final UUID accountId, final String bundleKey) throws EntitlementUserApiException {
+        final SubscriptionBundle bundle = entitlementUserApi.getBundleForAccountAndKey(accountId, bundleKey);
         return new BlockingSubscriptionBundle(bundle, blockingApi);
     }
 
@@ -80,6 +80,18 @@ public class BlockingEntitlementUserApi implements EntitlementUserApi {
     }
 
     @Override
+    public List<SubscriptionBundle> getBundlesForKey(String bundleKey)
+            throws EntitlementUserApiException {
+        final List<SubscriptionBundle> result = new ArrayList<SubscriptionBundle>();
+        final List<SubscriptionBundle> bundles = entitlementUserApi.getBundlesForKey(bundleKey);
+        for (final SubscriptionBundle bundle : bundles) {
+            result.add(new BlockingSubscriptionBundle(bundle, blockingApi));
+        }
+        return result;
+    }
+
+
+    @Override
     public List<Subscription> getSubscriptionsForBundle(final UUID bundleId) {
         final List<Subscription> result = new ArrayList<Subscription>();
         final List<Subscription> subscriptions = entitlementUserApi.getSubscriptionsForBundle(bundleId);
@@ -90,9 +102,9 @@ public class BlockingEntitlementUserApi implements EntitlementUserApi {
     }
 
     @Override
-    public List<Subscription> getSubscriptionsForKey(final String bundleKey) {
+    public List<Subscription> getSubscriptionsForAccountAndKey(final UUID accountId, final String bundleKey) {
         final List<Subscription> result = new ArrayList<Subscription>();
-        final List<Subscription> subscriptions = entitlementUserApi.getSubscriptionsForKey(bundleKey);
+        final List<Subscription> subscriptions = entitlementUserApi.getSubscriptionsForAccountAndKey(accountId, bundleKey);
         for (final Subscription subscription : subscriptions) {
             result.add(new BlockingSubscription(subscription, blockingApi, checker));
         }
diff --git a/junction/src/main/java/com/ning/billing/junction/plumbing/api/BlockingSubscription.java b/junction/src/main/java/com/ning/billing/junction/plumbing/api/BlockingSubscription.java
index 3a7f932..41a9015 100644
--- a/junction/src/main/java/com/ning/billing/junction/plumbing/api/BlockingSubscription.java
+++ b/junction/src/main/java/com/ning/billing/junction/plumbing/api/BlockingSubscription.java
@@ -67,7 +67,7 @@ public class BlockingSubscription implements Subscription {
 
     @Override
     public boolean changePlan(final String productName, final BillingPeriod term, final String priceList, final DateTime requestedDate,
-                              final CallContext context) throws EntitlementUserApiException {
+            final CallContext context) throws EntitlementUserApiException {
         try {
             checker.checkBlockedChange(this);
         } catch (BlockingApiException e) {
@@ -78,7 +78,7 @@ public class BlockingSubscription implements Subscription {
 
     @Override
     public boolean changePlanWithPolicy(final String productName, final BillingPeriod term, final String priceList,
-                                        final DateTime requestedDate, final ActionPolicy policy, final CallContext context) throws EntitlementUserApiException {
+            final DateTime requestedDate, final ActionPolicy policy, final CallContext context) throws EntitlementUserApiException {
         try {
             checker.checkBlockedChange(this);
         } catch (BlockingApiException e) {
@@ -89,7 +89,7 @@ public class BlockingSubscription implements Subscription {
 
     @Override
     public boolean recreate(final PlanPhaseSpecifier spec, final DateTime requestedDate, final CallContext context)
-            throws EntitlementUserApiException {
+    throws EntitlementUserApiException {
         return subscription.recreate(spec, requestedDate, context);
     }
 
@@ -104,6 +104,11 @@ public class BlockingSubscription implements Subscription {
     }
 
     @Override
+    public SubscriptionSourceType getSourceType() {
+        return subscription.getSourceType();
+    }
+
+    @Override
     public DateTime getStartDate() {
         return subscription.getStartDate();
     }
@@ -179,5 +184,4 @@ public class BlockingSubscription implements Subscription {
     public Subscription getDelegateSubscription() {
         return subscription;
     }
-
 }
diff --git a/server/src/test/java/com/ning/billing/jaxrs/TestBundle.java b/server/src/test/java/com/ning/billing/jaxrs/TestBundle.java
index 962c581..749e108 100644
--- a/server/src/test/java/com/ning/billing/jaxrs/TestBundle.java
+++ b/server/src/test/java/com/ning/billing/jaxrs/TestBundle.java
@@ -1,4 +1,4 @@
-/* 
+/*
  * Copyright 2010-2011 Ning, Inc.
  *
  * Ning licenses this file to you under the Apache License, version 2.0
@@ -45,7 +45,8 @@ public class TestBundle extends TestJaxrsBase {
         // Retrieves by external key
         final Map<String, String> queryParams = new HashMap<String, String>();
         queryParams.put(JaxrsResource.QUERY_EXTERNAL_KEY, "12345");
-        final Response response = doGet(JaxrsResource.BUNDLES_PATH, queryParams, DEFAULT_HTTP_TIMEOUT_SEC);
+        final String uri = JaxrsResource.ACCOUNTS_PATH + "/" + accountJson.getAccountId() + "/" + JaxrsResource.BUNDLES;
+        final Response response = doGet(uri, queryParams, DEFAULT_HTTP_TIMEOUT_SEC);
         Assert.assertEquals(response.getStatusCode(), Status.OK.getStatusCode());
         final String baseJson = response.getResponseBody();
         final BundleJsonNoSubscriptions objFromJson = mapper.readValue(baseJson, BundleJsonNoSubscriptions.class);
@@ -86,7 +87,8 @@ public class TestBundle extends TestJaxrsBase {
         // Retrieves by external key
         final Map<String, String> queryParams = new HashMap<String, String>();
         queryParams.put(JaxrsResource.QUERY_EXTERNAL_KEY, "56566");
-        response = doGet(JaxrsResource.BUNDLES_PATH, queryParams, DEFAULT_HTTP_TIMEOUT_SEC);
+        uri = JaxrsResource.ACCOUNTS_PATH + "/" + accountJson.getAccountId() + "/" + JaxrsResource.BUNDLES;
+        response = doGet(uri, queryParams, DEFAULT_HTTP_TIMEOUT_SEC);
         Assert.assertEquals(response.getStatusCode(), Status.NOT_FOUND.getStatusCode());
 
         uri = JaxrsResource.ACCOUNTS_PATH + "/" + accountJson.getAccountId() + "/" + JaxrsResource.BUNDLES;
diff --git a/util/src/test/java/com/ning/billing/api/TestApiListener.java b/util/src/test/java/com/ning/billing/api/TestApiListener.java
index 2425525..841cffb 100644
--- a/util/src/test/java/com/ning/billing/api/TestApiListener.java
+++ b/util/src/test/java/com/ning/billing/api/TestApiListener.java
@@ -56,6 +56,7 @@ public class TestApiListener {
         MIGRATE_ENTITLEMENT,
         MIGRATE_BILLING,
         CREATE,
+        TRANSFER,
         RE_CREATE,
         CHANGE,
         CANCEL,
@@ -86,10 +87,14 @@ public class TestApiListener {
     public void handleEntitlementEvents(final EffectiveSubscriptionEvent eventEffective) {
         log.info(String.format("Got subscription event %s", eventEffective.toString()));
         switch (eventEffective.getTransitionType()) {
-            case MIGRATE_ENTITLEMENT:
-                assertEqualsNicely(NextEvent.MIGRATE_ENTITLEMENT);
-                notifyIfStackEmpty();
-                break;
+        case TRANSFER:
+            assertEqualsNicely(NextEvent.TRANSFER);
+            notifyIfStackEmpty();
+            break;
+        case MIGRATE_ENTITLEMENT:
+            assertEqualsNicely(NextEvent.MIGRATE_ENTITLEMENT);
+            notifyIfStackEmpty();
+            break;
             case MIGRATE_BILLING:
                 assertEqualsNicely(NextEvent.MIGRATE_BILLING);
                 notifyIfStackEmpty();
diff --git a/util/src/test/java/com/ning/billing/mock/api/MockEntitlementUserApi.java b/util/src/test/java/com/ning/billing/mock/api/MockEntitlementUserApi.java
index 0fdeeb1..09a1820 100644
--- a/util/src/test/java/com/ning/billing/mock/api/MockEntitlementUserApi.java
+++ b/util/src/test/java/com/ning/billing/mock/api/MockEntitlementUserApi.java
@@ -89,6 +89,12 @@ public class MockEntitlementUserApi implements EntitlementUserApi {
     }
 
     @Override
+    public List<SubscriptionBundle> getBundlesForKey(String bundleKey)
+            throws EntitlementUserApiException {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
     public List<Subscription> getSubscriptionsForBundle(final UUID bundleId) {
         throw new UnsupportedOperationException();
     }
@@ -99,7 +105,7 @@ public class MockEntitlementUserApi implements EntitlementUserApi {
     }
 
     @Override
-    public List<Subscription> getSubscriptionsForKey(final String bundleKey) {
+    public List<Subscription> getSubscriptionsForAccountAndKey(final UUID accountId, final String bundleKey) {
         throw new UnsupportedOperationException();
     }
 
@@ -110,7 +116,7 @@ public class MockEntitlementUserApi implements EntitlementUserApi {
     }
 
     @Override
-    public SubscriptionBundle getBundleForKey(final String bundleKey) {
+    public SubscriptionBundle getBundleForAccountAndKey(final UUID accountId, final String bundleKey) {
         throw new UnsupportedOperationException();
     }
 
diff --git a/util/src/test/java/com/ning/billing/mock/glue/MockEntitlementModule.java b/util/src/test/java/com/ning/billing/mock/glue/MockEntitlementModule.java
index d712082..e408e14 100644
--- a/util/src/test/java/com/ning/billing/mock/glue/MockEntitlementModule.java
+++ b/util/src/test/java/com/ning/billing/mock/glue/MockEntitlementModule.java
@@ -23,6 +23,7 @@ import com.ning.billing.entitlement.api.EntitlementService;
 import com.ning.billing.entitlement.api.billing.ChargeThruApi;
 import com.ning.billing.entitlement.api.migration.EntitlementMigrationApi;
 import com.ning.billing.entitlement.api.timeline.EntitlementTimelineApi;
+import com.ning.billing.entitlement.api.transfer.EntitlementTransferApi;
 import com.ning.billing.entitlement.api.user.EntitlementUserApi;
 import com.ning.billing.glue.EntitlementModule;
 import com.ning.billing.util.glue.RealImplementation;
@@ -61,4 +62,10 @@ public class MockEntitlementModule extends AbstractModule implements Entitlement
     public void installEntitlementTimelineApi() {
         bind(EntitlementTimelineApi.class).toInstance(Mockito.mock(EntitlementTimelineApi.class));
     }
+
+    @Override
+    public void installEntitlementTransferApi() {
+        bind(EntitlementTransferApi.class).toInstance(Mockito.mock(EntitlementTransferApi.class));
+
+    }
 }
diff --git a/util/src/test/java/com/ning/billing/mock/MockSubscription.java b/util/src/test/java/com/ning/billing/mock/MockSubscription.java
index 013c9e0..57775aa 100644
--- a/util/src/test/java/com/ning/billing/mock/MockSubscription.java
+++ b/util/src/test/java/com/ning/billing/mock/MockSubscription.java
@@ -179,7 +179,11 @@ public class MockSubscription implements Subscription {
 
     @Override
     public DateTime getFutureEndDate() {
-        // TODO Auto-generated method stub
+        return null;
+    }
+
+    @Override
+    public SubscriptionSourceType getSourceType() {
         return null;
     }
 }