killbill-uncached

Merge branch 'integration' of github.com:ning/killbill

5/14/2012 8:30:42 PM

Changes

beatrix/pom.xml 29(+29 -0)

entitlement/pom.xml 54(+24 -30)

entitlement/src/test/java/com/ning/billing/entitlement/api/ApiTestListener.java 233(+0 -233)

invoice/pom.xml 45(+12 -33)

junction/pom.xml 14(+2 -12)

overdue/pom.xml 21(+1 -20)

payment/pom.xml 28(+11 -17)

pom.xml 12(+12 -0)

server/pom.xml 12(+8 -4)

Details

diff --git a/account/src/main/java/com/ning/billing/account/api/user/DefaultAccountCreationEvent.java b/account/src/main/java/com/ning/billing/account/api/user/DefaultAccountCreationEvent.java
index f944ccb..5dabd40 100644
--- a/account/src/main/java/com/ning/billing/account/api/user/DefaultAccountCreationEvent.java
+++ b/account/src/main/java/com/ning/billing/account/api/user/DefaultAccountCreationEvent.java
@@ -19,6 +19,7 @@ package com.ning.billing.account.api.user;
 import com.ning.billing.account.api.Account;
 import com.ning.billing.account.api.AccountCreationEvent;
 import com.ning.billing.account.api.AccountData;
+import com.ning.billing.account.api.DefaultAccount;
 import com.ning.billing.catalog.api.Currency;
 
 import java.util.UUID;
@@ -45,7 +46,7 @@ public class DefaultAccountCreationEvent implements AccountCreationEvent {
     
     public DefaultAccountCreationEvent(Account data, UUID userToken) {
         this.id = data.getId();
-        this.data = data;
+        this.data = new DefaultAccountData(data);
         this.userToken = userToken;
     }
 
@@ -131,6 +132,29 @@ public class DefaultAccountCreationEvent implements AccountCreationEvent {
         private final boolean isMigrated;
         private final boolean isNotifiedForInvoices;
         
+        
+        public DefaultAccountData(Account d) {
+            this(d.getExternalKey() != null ?  d.getExternalKey().toString() : null,
+                    d.getName(),
+                    d.getFirstNameLength(),
+                    d.getEmail(),
+                    d.getBillCycleDay(),
+                    d.getCurrency() != null ?  d.getCurrency().name() : null,
+                    d.getPaymentProviderName(), 
+                    d.getTimeZone() != null ?  d.getTimeZone().getID() : null,
+                    d.getLocale(),
+                    d.getAddress1(),
+                    d.getAddress2(),
+                    d.getCompanyName(),
+                    d.getCity(),
+                    d.getStateOrProvince(),
+                    d.getPostalCode(),
+                    d.getCountry(),
+                    d.getPhone(),
+                    d.isMigrated(),
+                    d.isNotifiedForInvoices());
+        }
+        
         @JsonCreator
         public DefaultAccountData(@JsonProperty("externalKey") String externalKey,
                 @JsonProperty("name") String name,
diff --git a/account/src/main/java/com/ning/billing/account/dao/AccountSqlDao.java b/account/src/main/java/com/ning/billing/account/dao/AccountSqlDao.java
index b9eaa99..8ed05ee 100644
--- a/account/src/main/java/com/ning/billing/account/dao/AccountSqlDao.java
+++ b/account/src/main/java/com/ning/billing/account/dao/AccountSqlDao.java
@@ -37,7 +37,7 @@ import org.skife.jdbi.v2.sqlobject.stringtemplate.ExternalizedSqlViaStringTempla
 
 @ExternalizedSqlViaStringTemplate3
 @RegisterMapper({UuidMapper.class, AccountMapper.class})
-public interface AccountSqlDao extends UpdatableEntitySqlDao<Account>, Transactional<AccountSqlDao>, Transmogrifier, AuditSqlDao {
+public interface AccountSqlDao extends UpdatableEntitySqlDao<Account>, Transactional<AccountSqlDao>, Transmogrifier {
     @SqlQuery
     public Account getAccountByKey(@Bind("externalKey") final String key);
 
diff --git a/account/src/main/java/com/ning/billing/account/dao/AuditedAccountDao.java b/account/src/main/java/com/ning/billing/account/dao/AuditedAccountDao.java
index 01c3dca..6d52d10 100644
--- a/account/src/main/java/com/ning/billing/account/dao/AuditedAccountDao.java
+++ b/account/src/main/java/com/ning/billing/account/dao/AuditedAccountDao.java
@@ -32,6 +32,9 @@ import com.ning.billing.util.tag.dao.TagDao;
 import org.skife.jdbi.v2.IDBI;
 import org.skife.jdbi.v2.Transaction;
 import org.skife.jdbi.v2.TransactionStatus;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
 import com.google.inject.Inject;
 import com.ning.billing.ErrorCode;
 import com.ning.billing.account.api.Account;
@@ -43,9 +46,12 @@ import com.ning.billing.account.api.user.DefaultAccountCreationEvent;
 import com.ning.billing.util.customfield.CustomField;
 import com.ning.billing.util.customfield.dao.CustomFieldSqlDao;
 import com.ning.billing.util.bus.Bus;
+import com.ning.billing.util.bus.Bus.EventBusException;
 import com.ning.billing.util.tag.Tag;
 
 public class AuditedAccountDao implements AccountDao {
+    private final static Logger log = LoggerFactory.getLogger(AuditedAccountDao.class);
+    
     private final AccountSqlDao accountSqlDao;
     private final TagDao tagDao;
     private final CustomFieldDao customFieldDao;
@@ -127,19 +133,23 @@ public class AuditedAccountDao implements AccountDao {
                     transactionalDao.create(account, context);
 
                     // insert history
-                    Long recordId = accountSqlDao.getRecordId(TableName.ACCOUNT, account.getId().toString());
+                    Long recordId = accountSqlDao.getRecordId(account.getId().toString());
                     EntityHistory<Account> history = new EntityHistory<Account>(account.getId(), recordId, account, ChangeType.INSERT);
                     accountSqlDao.insertHistoryFromTransaction(history, context);
 
                     // insert audit
-                    Long historyRecordId = accountSqlDao.getHistoryRecordId(TableName.ACCOUNT_HISTORY, recordId);
-                    EntityAudit audit = new EntityAudit(historyRecordId, ChangeType.INSERT);
-                    accountSqlDao.insertAuditFromTransaction(TableName.ACCOUNT_HISTORY, audit, context);
+                    Long historyRecordId = accountSqlDao.getHistoryRecordId(recordId);
+                    EntityAudit audit = new EntityAudit(TableName.ACCOUNT_HISTORY, historyRecordId, ChangeType.INSERT);
+                    accountSqlDao.insertAuditFromTransaction(audit, context);
 
                     saveTagsFromWithinTransaction(account, transactionalDao, context);
                     saveCustomFieldsFromWithinTransaction(account, transactionalDao, context);
                     AccountCreationEvent creationEvent = new DefaultAccountCreationEvent(account, context.getUserToken());
-                    eventBus.post(creationEvent);
+                    try {
+                        eventBus.postFromTransaction(creationEvent, transactionalDao);
+                    } catch (EventBusException e) {
+                        log.warn("Failed to post account creation event for account " + account.getId(), e);
+                    }
                     return null;
                 }
             });
@@ -159,9 +169,9 @@ public class AuditedAccountDao implements AccountDao {
         try {
             accountSqlDao.inTransaction(new Transaction<Void, AccountSqlDao>() {
                 @Override
-                public Void inTransaction(final AccountSqlDao accountSqlDao, final TransactionStatus status) throws EntityPersistenceException, Bus.EventBusException {
+                public Void inTransaction(final AccountSqlDao transactional, final TransactionStatus status) throws EntityPersistenceException, Bus.EventBusException {
                     String accountId = account.getId().toString();
-                    Account currentAccount = accountSqlDao.getById(accountId);
+                    Account currentAccount = transactional.getById(accountId);
                     if (currentAccount == null) {
                         throw new EntityPersistenceException(ErrorCode.ACCOUNT_DOES_NOT_EXIST_FOR_ID, accountId);
                     }
@@ -171,22 +181,26 @@ public class AuditedAccountDao implements AccountDao {
                         throw new EntityPersistenceException(ErrorCode.ACCOUNT_CANNOT_CHANGE_EXTERNAL_KEY, currentKey);
                     }
 
-                    accountSqlDao.update(account, context);
+                    transactional.update(account, context);
 
-                    Long recordId = accountSqlDao.getRecordId(TableName.ACCOUNT, account.getId().toString());
+                    Long recordId = accountSqlDao.getRecordId(account.getId().toString());
                     EntityHistory<Account> history = new EntityHistory<Account>(account.getId(), recordId, account, ChangeType.INSERT);
                     accountSqlDao.insertHistoryFromTransaction(history, context);
 
-                    Long historyRecordId = accountSqlDao.getHistoryRecordId(TableName.ACCOUNT_HISTORY, recordId);
-                    EntityAudit audit = new EntityAudit(historyRecordId, ChangeType.INSERT);
-                    accountSqlDao.insertAuditFromTransaction(TableName.ACCOUNT_HISTORY, audit, context);
+                    Long historyRecordId = accountSqlDao.getHistoryRecordId(recordId);
+                    EntityAudit audit = new EntityAudit(TableName.ACCOUNT_HISTORY, historyRecordId, ChangeType.INSERT);
+                    accountSqlDao.insertAuditFromTransaction(audit, context);
 
-                    saveTagsFromWithinTransaction(account, accountSqlDao, context);
-                    saveCustomFieldsFromWithinTransaction(account, accountSqlDao, context);
+                    saveTagsFromWithinTransaction(account, transactional, context);
+                    saveCustomFieldsFromWithinTransaction(account, transactional, context);
 
                     AccountChangeEvent changeEvent = new DefaultAccountChangeEvent(account.getId(), context.getUserToken(), currentAccount, account);
                     if (changeEvent.hasChanges()) {
-                        eventBus.post(changeEvent);
+                        try {
+                            eventBus.postFromTransaction(changeEvent, transactional);
+                        } catch (EventBusException e) {
+                            log.warn("Failed to post account change event for account " + account.getId(), e);
+                        }
                     }
                     return null;
                 }
diff --git a/account/src/main/java/com/ning/billing/account/glue/AccountModule.java b/account/src/main/java/com/ning/billing/account/glue/AccountModule.java
index e3009e8..956d878 100644
--- a/account/src/main/java/com/ning/billing/account/glue/AccountModule.java
+++ b/account/src/main/java/com/ning/billing/account/glue/AccountModule.java
@@ -23,15 +23,17 @@ import com.ning.billing.account.api.AccountUserApi;
 import com.ning.billing.account.api.DefaultAccountService;
 import com.ning.billing.account.api.user.DefaultAccountUserApi;
 import com.ning.billing.account.dao.AccountDao;
+import com.ning.billing.account.dao.AccountEmailDao;
 import com.ning.billing.account.dao.AuditedAccountDao;
+import com.ning.billing.account.dao.AuditedAccountEmailDao;
 import com.ning.billing.util.glue.RealImplementation;
 
 public class AccountModule extends AbstractModule {
-
     private void installConfig() {
     }
 
     protected void installAccountDao() {
+        bind(AccountEmailDao.class).to(AuditedAccountEmailDao.class).asEagerSingleton();
         bind(AccountDao.class).to(AuditedAccountDao.class).asEagerSingleton();
     }
 
diff --git a/account/src/test/java/com/ning/billing/account/dao/AccountDaoTestBase.java b/account/src/test/java/com/ning/billing/account/dao/AccountDaoTestBase.java
index fb3c480..9eb462f 100644
--- a/account/src/test/java/com/ning/billing/account/dao/AccountDaoTestBase.java
+++ b/account/src/test/java/com/ning/billing/account/dao/AccountDaoTestBase.java
@@ -101,6 +101,8 @@ public abstract class AccountDaoTestBase {
             public Void inTransaction(Handle h, TransactionStatus status) throws Exception {
                 h.execute("truncate table accounts");
                 h.execute("truncate table notifications");
+                h.execute("truncate table bus_events");
+                h.execute("truncate table claimed_bus_events");                              
                 h.execute("truncate table claimed_notifications");
                 h.execute("truncate table tag_definitions");
                 h.execute("truncate table tags");
diff --git a/account/src/test/java/com/ning/billing/account/glue/AccountModuleWithMocks.java b/account/src/test/java/com/ning/billing/account/glue/AccountModuleWithMocks.java
index 74fe321..1559420 100644
--- a/account/src/test/java/com/ning/billing/account/glue/AccountModuleWithMocks.java
+++ b/account/src/test/java/com/ning/billing/account/glue/AccountModuleWithMocks.java
@@ -17,7 +17,9 @@
 package com.ning.billing.account.glue;
 
 import com.ning.billing.account.dao.AccountDao;
+import com.ning.billing.account.dao.AccountEmailDao;
 import com.ning.billing.account.dao.MockAccountDao;
+import com.ning.billing.mock.BrainDeadProxyFactory;
 import com.ning.billing.util.clock.MockClockModule;
 import com.ning.billing.util.glue.CallContextModule;
 import com.ning.billing.util.glue.FieldStoreModule;
@@ -27,6 +29,8 @@ public class AccountModuleWithMocks extends AccountModule {
     @Override
     protected void installAccountDao() {
         bind(MockAccountDao.class).asEagerSingleton();
+        AccountEmailDao accountEmailDao = BrainDeadProxyFactory.createBrainDeadProxyFor(AccountEmailDao.class);
+        bind(AccountEmailDao.class).toInstance(accountEmailDao);
         bind(AccountDao.class).to(MockAccountDao.class);
     }
 
diff --git a/analytics/src/main/java/com/ning/billing/analytics/BusinessAccountRecorder.java b/analytics/src/main/java/com/ning/billing/analytics/BusinessAccountRecorder.java
index aa6d6fa..e692eb7 100644
--- a/analytics/src/main/java/com/ning/billing/analytics/BusinessAccountRecorder.java
+++ b/analytics/src/main/java/com/ning/billing/analytics/BusinessAccountRecorder.java
@@ -35,6 +35,7 @@ import com.ning.billing.analytics.dao.BusinessAccountDao;
 import com.ning.billing.invoice.api.Invoice;
 import com.ning.billing.invoice.api.InvoiceUserApi;
 import com.ning.billing.payment.api.PaymentApi;
+import com.ning.billing.payment.api.PaymentApiException;
 import com.ning.billing.payment.api.PaymentAttempt;
 import com.ning.billing.payment.api.PaymentInfoEvent;
 import com.ning.billing.util.tag.Tag;
@@ -59,17 +60,17 @@ public class BusinessAccountRecorder {
         Account account;
         try {
             account = accountApi.getAccountByKey(data.getExternalKey());
-        final BusinessAccount bac = createBusinessAccountFromAccount(account);
+            final BusinessAccount bac = createBusinessAccountFromAccount(account);
 
-        log.info("ACCOUNT CREATION " + bac);
-        dao.createAccount(bac);
+            log.info("ACCOUNT CREATION " + bac);
+            dao.createAccount(bac);
         } catch (AccountApiException e) {
-           log.warn("Error encountered creating BusinessAccount",e);
+            log.warn("Error encountered creating BusinessAccount",e);
         }
-   }
+    }
 
     /**
-     * Notification handler for ACCOUNT changes
+     * Notification handler for Account changes
      *
      * @param accountId     account id changed
      * @param changedFields list of changed fields
@@ -85,17 +86,19 @@ public class BusinessAccountRecorder {
      * @param paymentInfo payment object (from the payment plugin)
      */
     public void accountUpdated(final PaymentInfoEvent paymentInfo) {
-        final PaymentAttempt paymentAttempt = paymentApi.getPaymentAttemptForPaymentId(paymentInfo.getPaymentId());
-        if (paymentAttempt == null) {
-            return;
-        }
         try {
+            final PaymentAttempt paymentAttempt = paymentApi.getPaymentAttemptForPaymentId(paymentInfo.getId());
+            if (paymentAttempt == null) {
+                return;
+            }
+
             final Account account = accountApi.getAccountById(paymentAttempt.getAccountId());
             accountUpdated(account.getId());
         } catch (AccountApiException e) {
             log.warn("Error encountered creating BusinessAccount",e);
+        } catch (PaymentApiException e) {
+            log.warn("Error encountered creating BusinessAccount",e);            
         }
-
     }
 
     /**
@@ -173,21 +176,25 @@ public class BusinessAccountRecorder {
             }
 
             // Retrieve payments information for these invoices
-            final PaymentInfoEvent payment = paymentApi.getLastPaymentInfo(invoiceIds);
-
-            lastPaymentStatus = payment.getStatus();
-            paymentMethod = payment.getPaymentMethod();
-            creditCardType = payment.getCardType();
-            billingAddressCountry = payment.getCardCountry();
+            try {
+                final PaymentInfoEvent payment = paymentApi.getLastPaymentInfo(invoiceIds);
+
+                lastPaymentStatus = payment.getStatus();
+                paymentMethod = payment.getPaymentMethod();
+                creditCardType = payment.getCardType();
+                billingAddressCountry = payment.getCardCountry();
+
+                bac.setLastPaymentStatus(lastPaymentStatus);
+                bac.setPaymentMethod(paymentMethod);
+                bac.setCreditCardType(creditCardType);
+                bac.setBillingAddressCountry(billingAddressCountry);
+                bac.setLastInvoiceDate(lastInvoiceDate);
+                bac.setTotalInvoiceBalance(totalInvoiceBalance);
+
+                bac.setBalance(invoiceUserApi.getAccountBalance(account.getId()));
+            } catch (PaymentApiException ex) {
+                // TODO: handle this exception
+            }
         }
-
-        bac.setLastPaymentStatus(lastPaymentStatus);
-        bac.setPaymentMethod(paymentMethod);
-        bac.setCreditCardType(creditCardType);
-        bac.setBillingAddressCountry(billingAddressCountry);
-        bac.setLastInvoiceDate(lastInvoiceDate);
-        bac.setTotalInvoiceBalance(totalInvoiceBalance);
-
-        bac.setBalance(invoiceUserApi.getAccountBalance(account.getId()));
     }
 }
diff --git a/analytics/src/main/java/com/ning/billing/analytics/BusinessSubscriptionTransitionRecorder.java b/analytics/src/main/java/com/ning/billing/analytics/BusinessSubscriptionTransitionRecorder.java
index 57d164f..0044758 100644
--- a/analytics/src/main/java/com/ning/billing/analytics/BusinessSubscriptionTransitionRecorder.java
+++ b/analytics/src/main/java/com/ning/billing/analytics/BusinessSubscriptionTransitionRecorder.java
@@ -98,13 +98,13 @@ public class BusinessSubscriptionTransitionRecorder
         final SubscriptionBundle bundle = entitlementApi.getBundleFromId(transition.getBundleId());
         if (bundle != null) {
             transitionKey = bundle.getKey();
-
+            
             final Account account = accountApi.getAccountById(bundle.getAccountId());
             if (account != null) {
                 accountKey = account.getExternalKey();
                 currency = account.getCurrency();
             }
-        }
+        }   
 
         // The ISubscriptionTransition interface gives us all the prev/next information we need but the start date
         // of the previous plan. We need to retrieve it from our own transitions table
diff --git a/analytics/src/test/java/com/ning/billing/analytics/AnalyticsTestModule.java b/analytics/src/test/java/com/ning/billing/analytics/AnalyticsTestModule.java
index cbab941..e1df28b 100644
--- a/analytics/src/test/java/com/ning/billing/analytics/AnalyticsTestModule.java
+++ b/analytics/src/test/java/com/ning/billing/analytics/AnalyticsTestModule.java
@@ -16,22 +16,22 @@
 
 package com.ning.billing.analytics;
 
-import com.ning.billing.util.email.EmailModule;
-import com.ning.billing.util.glue.GlobalLockerModule;
 import org.skife.jdbi.v2.IDBI;
 
 import com.ning.billing.account.glue.AccountModule;
 import com.ning.billing.analytics.setup.AnalyticsModule;
 import com.ning.billing.dbi.MysqlTestingHelper;
-import com.ning.billing.entitlement.glue.EntitlementModule;
-import com.ning.billing.invoice.glue.InvoiceModule;
-import com.ning.billing.junction.MockBlockingModule;
-import com.ning.billing.junction.glue.JunctionModule;
+import com.ning.billing.entitlement.glue.DefaultEntitlementModule;
+import com.ning.billing.invoice.glue.DefaultInvoiceModule;
+import com.ning.billing.junction.glue.DefaultJunctionModule;
 import com.ning.billing.payment.setup.PaymentModule;
+import com.ning.billing.util.email.EmailModule;
+import com.ning.billing.util.email.templates.TemplateModule;
 import com.ning.billing.util.glue.BusModule;
 import com.ning.billing.util.glue.CallContextModule;
 import com.ning.billing.util.glue.ClockModule;
 import com.ning.billing.util.glue.FieldStoreModule;
+import com.ning.billing.util.glue.GlobalLockerModule;
 import com.ning.billing.util.glue.NotificationQueueModule;
 import com.ning.billing.util.glue.TagStoreModule;
 import com.ning.billing.util.tag.dao.TagDefinitionSqlDao;
@@ -52,12 +52,13 @@ public class AnalyticsTestModule extends AnalyticsModule
         install(new TagStoreModule());
         install(new AccountModule());
         install(new BusModule());
-        install(new EntitlementModule());
-        install(new InvoiceModule());
+        install(new DefaultEntitlementModule());
+        install(new DefaultInvoiceModule());
+        install(new TemplateModule());
         install(new PaymentModule());
         install(new TagStoreModule());
         install(new NotificationQueueModule());
-        install(new JunctionModule());
+        install(new DefaultJunctionModule());
 
         // Install the Dao layer
         final MysqlTestingHelper helper = new MysqlTestingHelper();
diff --git a/analytics/src/test/java/com/ning/billing/analytics/api/TestAnalyticsService.java b/analytics/src/test/java/com/ning/billing/analytics/api/TestAnalyticsService.java
index 0a01c34..1477dfa 100644
--- a/analytics/src/test/java/com/ning/billing/analytics/api/TestAnalyticsService.java
+++ b/analytics/src/test/java/com/ning/billing/analytics/api/TestAnalyticsService.java
@@ -33,7 +33,6 @@ import org.joda.time.DateTime;
 import org.testng.Assert;
 import org.testng.annotations.AfterClass;
 import org.testng.annotations.BeforeClass;
-import org.testng.annotations.BeforeMethod;
 import org.testng.annotations.Guice;
 import org.testng.annotations.Test;
 
@@ -157,15 +156,7 @@ public class TestAnalyticsService {
     private  CatalogService catalogService;
     
     private Catalog catalog;
-    
-    @BeforeMethod(groups = "slow")
-    public void cleanup() throws Exception
-    {
-        helper.cleanupTable("bst");
-        helper.cleanupTable("bac");        
-    }
-
-
+ 
     @BeforeClass(groups = "slow")
     public void startMysql() throws IOException, ClassNotFoundException, SQLException, EntitlementUserApiException {
 
@@ -176,6 +167,8 @@ public class TestAnalyticsService {
         // Killbill generic setup
         setupBusAndMySQL();
 
+        helper.cleanupAllTables();
+        
         tagDao.create(TAG_ONE, context);
         tagDao.create(TAG_TWO, context);
 
@@ -197,7 +190,6 @@ public class TestAnalyticsService {
     }
 
     private void setupBusAndMySQL() throws IOException {
-        bus.start();
 
         final String analyticsDdl = IOUtils.toString(BusinessSubscriptionTransitionDao.class.getResourceAsStream("/com/ning/billing/analytics/ddl.sql"));
         final String accountDdl = IOUtils.toString(BusinessSubscriptionTransitionDao.class.getResourceAsStream("/com/ning/billing/account/ddl.sql"));
@@ -216,7 +208,9 @@ public class TestAnalyticsService {
         helper.initDb(utilDdl);
         helper.initDb(junctionDdl);
 
-    	helper.cleanupAllTables();
+        helper.cleanupAllTables();
+    	
+        bus.start();
     }
 
     private void createSubscriptionTransitionEvent(final Account account) throws EntitlementUserApiException {
@@ -283,9 +277,9 @@ public class TestAnalyticsService {
         invoiceCreationNotification = new DefaultInvoiceCreationEvent(invoice.getId(), account.getId(),
                 INVOICE_AMOUNT, ACCOUNT_CURRENCY, clock.getUTCNow(), null);
 
-        paymentInfoNotification = new DefaultPaymentInfoEvent.Builder().setPaymentId(UUID.randomUUID().toString()).setPaymentMethod(PAYMENT_METHOD).setCardCountry(CARD_COUNTRY).build();
+        paymentInfoNotification = new DefaultPaymentInfoEvent.Builder().setId(UUID.randomUUID()).setPaymentMethod(PAYMENT_METHOD).setCardCountry(CARD_COUNTRY).build();
         final PaymentAttempt paymentAttempt = new DefaultPaymentAttempt(UUID.randomUUID(), invoice.getId(), account.getId(), BigDecimal.TEN,
-                ACCOUNT_CURRENCY, clock.getUTCNow(), clock.getUTCNow(), paymentInfoNotification.getPaymentId(), 1);
+                ACCOUNT_CURRENCY, clock.getUTCNow(), clock.getUTCNow(), paymentInfoNotification.getId(), 1, null, null);
         paymentDao.createPaymentAttempt(paymentAttempt, context);
         paymentDao.savePaymentInfo(paymentInfoNotification, context);
         Assert.assertEquals(paymentDao.getPaymentInfoList(Arrays.asList(invoice.getId().toString())).size(), 1);
@@ -296,7 +290,7 @@ public class TestAnalyticsService {
         helper.stopMysql();
     }
 
-    @Test(groups = "slow")
+    @Test(groups = "slow", enabled=true)
     public void testRegisterForNotifications() throws Exception {
         // Make sure the service has been instantiated
         Assert.assertEquals(service.getName(), "analytics-service");
@@ -312,9 +306,8 @@ public class TestAnalyticsService {
 
         // Send events and wait for the async part...
         bus.post(transition);
-
         bus.post(accountCreationNotification);
-        Thread.sleep(1000);
+        Thread.sleep(5000);
 
         Assert.assertEquals(subscriptionDao.getTransitions(KEY).size(), 1);
         Assert.assertEquals(subscriptionDao.getTransitions(KEY).get(0), expectedTransition);
@@ -329,12 +322,12 @@ public class TestAnalyticsService {
 
         // Post the same invoice event again - the invoice balance shouldn't change
         bus.post(invoiceCreationNotification);
-        Thread.sleep(1000);
+        Thread.sleep(5000);
         Assert.assertTrue(accountDao.getAccount(ACCOUNT_KEY).getTotalInvoiceBalance().compareTo(INVOICE_AMOUNT) == 0);
 
         // Test payment integration - the fields have already been populated, just make sure the code is exercised
         bus.post(paymentInfoNotification);
-        Thread.sleep(1000);
+        Thread.sleep(5000);
         Assert.assertEquals(accountDao.getAccount(ACCOUNT_KEY).getPaymentMethod(), PAYMENT_METHOD);
         Assert.assertEquals(accountDao.getAccount(ACCOUNT_KEY).getBillingAddressCountry(), CARD_COUNTRY);
 
diff --git a/analytics/src/test/java/com/ning/billing/analytics/dao/TestAnalyticsDao.java b/analytics/src/test/java/com/ning/billing/analytics/dao/TestAnalyticsDao.java
index 079df6e..d8c856d 100644
--- a/analytics/src/test/java/com/ning/billing/analytics/dao/TestAnalyticsDao.java
+++ b/analytics/src/test/java/com/ning/billing/analytics/dao/TestAnalyticsDao.java
@@ -263,10 +263,8 @@ public class TestAnalyticsDao
         Assert.assertEquals(transitions.get(0).getKey(), transition.getKey());
         Assert.assertEquals(transitions.get(0).getRequestedTimestamp(), transition.getRequestedTimestamp());
         Assert.assertEquals(transitions.get(0).getEvent(), transition.getEvent());
-        // Null Plan and Phase doesn't make sense so we turn the subscription into a null
-        // STEPH not sure why that fails ?
-        //Assert.assertNull(transitions.get(0).getPreviousSubscription());
-        //Assert.assertNull(transitions.get(0).getNextSubscription());
+        Assert.assertNull(transitions.get(0).getPreviousSubscription());
+        Assert.assertNull(transitions.get(0).getNextSubscription());
     }
 
     @Test(groups = "slow")
diff --git a/analytics/src/test/java/com/ning/billing/analytics/MockEntitlementUserApi.java b/analytics/src/test/java/com/ning/billing/analytics/MockEntitlementUserApi.java
index 3a2baac..725c62b 100644
--- a/analytics/src/test/java/com/ning/billing/analytics/MockEntitlementUserApi.java
+++ b/analytics/src/test/java/com/ning/billing/analytics/MockEntitlementUserApi.java
@@ -28,6 +28,7 @@ import com.ning.billing.entitlement.api.user.EntitlementUserApi;
 import com.ning.billing.entitlement.api.user.EntitlementUserApiException;
 import com.ning.billing.entitlement.api.user.Subscription;
 import com.ning.billing.entitlement.api.user.SubscriptionBundle;
+import com.ning.billing.entitlement.api.user.SubscriptionStatusDryRun;
 import com.ning.billing.junction.api.BlockingState;
 import com.ning.billing.overdue.OverdueState;
 import com.ning.billing.util.callcontext.CallContext;
@@ -136,4 +137,11 @@ public class MockEntitlementUserApi implements EntitlementUserApi
     public Subscription getBaseSubscription(UUID bundleId) {
         throw new UnsupportedOperationException();
     }
+
+    @Override
+    public List<SubscriptionStatusDryRun> getDryRunChangePlanStatus(
+            UUID subscriptionId, String productName, DateTime requestedDate)
+            throws EntitlementUserApiException {
+        throw new UnsupportedOperationException();
+    }
 }
diff --git a/api/src/main/java/com/ning/billing/entitlement/api/billing/ChargeThruApi.java b/api/src/main/java/com/ning/billing/entitlement/api/billing/ChargeThruApi.java
index 0dec9b9..69eb454 100644
--- a/api/src/main/java/com/ning/billing/entitlement/api/billing/ChargeThruApi.java
+++ b/api/src/main/java/com/ning/billing/entitlement/api/billing/ChargeThruApi.java
@@ -29,7 +29,7 @@ public interface ChargeThruApi {
      * @param subscriptionId
      * @return UUID of
      */
-    public UUID getAccountIdFromSubscriptionId(UUID subscriptionId);
+    public UUID getAccountIdFromSubscriptionId(UUID subscriptionId) throws EntitlementBillingApiException;
     
     /**
      * Sets the charged through date for the subscription with that Id.
@@ -39,16 +39,4 @@ public interface ChargeThruApi {
      * @param context
      */
     public void setChargedThroughDate(UUID subscriptionId, DateTime ctd, CallContext context);
-
-    /**
-     * Sets the charged through date for the subscription with that Id. Within the context of a SQL Transaction
-     * 
-     * @param transactionalDao
-     * @param subscriptionId
-     * @param ctd
-     * @param context
-     */
-    public void setChargedThroughDateFromTransaction(Transmogrifier transactionalDao, UUID subscriptionId,
-                                                     DateTime ctd, CallContext context);
-
 }
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 0e74f86..1e8d7fd 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
@@ -21,6 +21,8 @@ import java.util.UUID;
 
 import com.ning.billing.util.callcontext.CallContext;
 import org.joda.time.DateTime;
+
+import com.ning.billing.catalog.api.BillingPeriod;
 import com.ning.billing.catalog.api.PlanPhaseSpecifier;
 
 
@@ -38,7 +40,7 @@ public interface EntitlementUserApi {
 
     public List<Subscription> getSubscriptionsForKey(String bundleKey);
     
-    public Subscription getBaseSubscription(UUID bundleId);
+    public Subscription getBaseSubscription(UUID bundleId) throws EntitlementUserApiException;
 
     public SubscriptionBundle createBundleForAccount(UUID accountId, String bundleKey, CallContext context)
         throws EntitlementUserApiException;
@@ -46,5 +48,8 @@ public interface EntitlementUserApi {
     public Subscription createSubscription(UUID bundleId, PlanPhaseSpecifier spec, DateTime requestedDate, CallContext context)
         throws EntitlementUserApiException;
 
+    public List<SubscriptionStatusDryRun> getDryRunChangePlanStatus(UUID subscriptionId, String productName, DateTime requestedDate)
+    throws EntitlementUserApiException;
+    
     public DateTime getNextBillingDate(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 0dbada0..af8e237 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
@@ -41,7 +41,7 @@ public interface Subscription extends ExtendedEntity, Blockable {
     throws EntitlementUserApiException;
 
     public boolean changePlan(String productName, BillingPeriod term, String planSet, DateTime requestedDate, CallContext context)
-        throws EntitlementUserApiException;
+    throws EntitlementUserApiException;
 
     public boolean recreate(PlanPhaseSpecifier spec, DateTime requestedDate, CallContext context)
         throws EntitlementUserApiException;
diff --git a/api/src/main/java/com/ning/billing/entitlement/api/user/SubscriptionEvent.java b/api/src/main/java/com/ning/billing/entitlement/api/user/SubscriptionEvent.java
index 8dc3312..2745c33 100644
--- a/api/src/main/java/com/ning/billing/entitlement/api/user/SubscriptionEvent.java
+++ b/api/src/main/java/com/ning/billing/entitlement/api/user/SubscriptionEvent.java
@@ -65,4 +65,5 @@ public interface SubscriptionEvent extends BusEvent {
     Integer getRemainingEventsForUserOperation();
     
     Long getTotalOrdering();
+    
 }
diff --git a/api/src/main/java/com/ning/billing/entitlement/api/user/SubscriptionStatusDryRun.java b/api/src/main/java/com/ning/billing/entitlement/api/user/SubscriptionStatusDryRun.java
new file mode 100644
index 0000000..36048fa
--- /dev/null
+++ b/api/src/main/java/com/ning/billing/entitlement/api/user/SubscriptionStatusDryRun.java
@@ -0,0 +1,42 @@
+/* 
+ * 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.user;
+
+import java.util.UUID;
+
+import com.ning.billing.catalog.api.BillingPeriod;
+import com.ning.billing.catalog.api.PhaseType;
+
+public interface SubscriptionStatusDryRun {
+
+    public UUID getId();
+    
+    public String getProductName();
+    
+    public BillingPeriod getBillingPeriod();
+    
+    public String getPriceList();
+    
+    public PhaseType getPhaseType();
+
+    public DryRunChangeReason getReason();
+    
+    public enum DryRunChangeReason {
+        AO_INCLUDED_IN_NEW_PLAN,
+        AO_NOT_AVAILABLE_IN_NEW_PLAN,
+        AO_AVAILABLE_IN_NEW_PLAN
+    }
+}
diff --git a/api/src/main/java/com/ning/billing/ErrorCode.java b/api/src/main/java/com/ning/billing/ErrorCode.java
index f6856eb..3aa190b 100644
--- a/api/src/main/java/com/ning/billing/ErrorCode.java
+++ b/api/src/main/java/com/ning/billing/ErrorCode.java
@@ -46,6 +46,8 @@ public enum ErrorCode {
     /* Change plan */
     ENT_CHANGE_NON_ACTIVE(1021, "Subscription %s is in state %s: Failed to change plan"),
     ENT_CHANGE_FUTURE_CANCELLED(1022, "Subscription %s is future cancelled: Failed to change plan"),
+    ENT_CHANGE_DRY_RUN_NOT_BP(1022, "Change DryRun API is only available for BP"),
+    
     /* Cancellation */
     ENT_CANCEL_BAD_STATE(1031, "Subscription %s is in state %s: Failed to cancel"),
     /* Recreation */
@@ -59,6 +61,7 @@ public enum ErrorCode {
     ENT_GET_INVALID_BUNDLE_ID(1081, "Could not find a bundle matching id %s"),
     ENT_INVALID_SUBSCRIPTION_ID(1082, "Unknown subscription %s"),
     ENT_GET_INVALID_BUNDLE_KEY(1083, "Could not find a bundle matching key %s"),
+    ENT_GET_NO_SUCH_BASE_SUBSCRIPTION(1084, "Could not base subscription for bundle %s"),
 
     /* Repair */
     ENT_REPAIR_INVALID_DELETE_SET(1091, "Event %s is not deleted for subscription %s but prior events were"),
@@ -80,6 +83,8 @@ public enum ErrorCode {
     
     ENT_BUNDLE_IS_OVERDUE_BLOCKED(1090, "Changes to this bundle are blocked by overdue enforcement (%s :  %s)"),
     ENT_ACCOUNT_IS_OVERDUE_BLOCKED(1091, "Changes to this account are blocked by overdue enforcement (%s)"),
+ 
+    
     /*
     *
     * Range 2000 : CATALOG
@@ -196,7 +201,25 @@ public enum ErrorCode {
     BLOCK_BLOCKED_ACTION(6000, "The action %s is block on this %s with id=%s"),
     BLOCK_TYPE_NOT_SUPPORTED(6001, "The Blockable type '%s' is not supported"),
     
-   /*
+    
+    /*
+     * Range 7000 : Payment
+     */
+    PAYMENT_NO_SUCH_PAYMENT_METHOD(7001, "Payment method for account %s, and paymentId %s does not exist"),
+    PAYMENT_NO_PAYMENT_METHODS(7002, "Payment methods for account %s don't exist"),
+    PAYMENT_UPD_GATEWAY_FAILED(7003, "Failed to update payment gateway for account %s : %s"),
+    PAYMENT_GET_PAYMENT_PROVIDER(7004, "Failed to retrieve payment provider for account %s : %s"),    
+    PAYMENT_ADD_PAYMENT_METHOD(7005, "Failed to add payment method for account %s : %s"),        
+    PAYMENT_DEL_PAYMENT_METHOD(7006, "Failed to delete payment method for account %s : %s"),        
+    PAYMENT_UPD_PAYMENT_METHOD(7007, "Failed to update payment method for account %s : %s"),            
+    PAYMENT_CREATE_PAYMENT(7008, "Failed to create payment for account %s : %s"),                
+    PAYMENT_CREATE_PAYMENT_FOR_ATTEMPT(7009, "Failed to create payment for account %s and attempt %s : %s"),                    
+    PAYMENT_CREATE_PAYMENT_FOR_ATTEMPT_WITH_NON_POSITIVE_INV(70010, "Got payment attempt with negative or null invoice for account %s"),                        
+    PAYMENT_CREATE_PAYMENT_FOR_ATTEMPT_BAD(7011, "Failed to create payment for attempts %s "),                    
+    PAYMENT_CREATE_PAYMENT_PROVIDER_ACCOUNT(7012, "Failed to create payment provider account for account %s : %s"),                
+    PAYMENT_UPD_PAYMENT_PROVIDER_ACCOUNT(7013, "Failed to update payment provider account for account %s : %s"),                    
+    PAYMENT_CREATE_REFUND(7014, "Failed to create refund for account %s : %s"),                
+    /*
     *
     * Range 9000: Miscellaneous
     *
diff --git a/api/src/main/java/com/ning/billing/glue/EntitlementModule.java b/api/src/main/java/com/ning/billing/glue/EntitlementModule.java
new file mode 100644
index 0000000..421e68d
--- /dev/null
+++ b/api/src/main/java/com/ning/billing/glue/EntitlementModule.java
@@ -0,0 +1,31 @@
+/*
+ * 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.glue;
+
+public interface EntitlementModule {
+
+    public abstract void installEntitlementService();
+
+    public abstract void installEntitlementUserApi();
+
+    public abstract void installEntitlementMigrationApi();
+
+    public abstract void installChargeThruApi();
+
+    public abstract void installEntitlementTimelineApi();
+
+}
\ No newline at end of file
diff --git a/api/src/main/java/com/ning/billing/invoice/api/InvoicePaymentApi.java b/api/src/main/java/com/ning/billing/invoice/api/InvoicePaymentApi.java
index 9fb0bb3..0a34604 100644
--- a/api/src/main/java/com/ning/billing/invoice/api/InvoicePaymentApi.java
+++ b/api/src/main/java/com/ning/billing/invoice/api/InvoicePaymentApi.java
@@ -42,7 +42,7 @@ public interface InvoicePaymentApi {
     public void notifyOfPaymentAttempt(InvoicePayment invoicePayment, CallContext context);
 
     public void notifyOfPaymentAttempt(UUID invoiceId, BigDecimal amountOutstanding, Currency currency, UUID paymentAttemptId, DateTime paymentAttemptDate, CallContext context);
-
+    
     public void notifyOfPaymentAttempt(UUID invoiceId, UUID paymentAttemptId, DateTime paymentAttemptDate, CallContext context);
 
 }
diff --git a/api/src/main/java/com/ning/billing/overdue/OverdueUserApi.java b/api/src/main/java/com/ning/billing/overdue/OverdueUserApi.java
index 4399176..74764b3 100644
--- a/api/src/main/java/com/ning/billing/overdue/OverdueUserApi.java
+++ b/api/src/main/java/com/ning/billing/overdue/OverdueUserApi.java
@@ -17,13 +17,14 @@
 package com.ning.billing.overdue;
 
 import com.ning.billing.catalog.api.CatalogApiException;
+import com.ning.billing.entitlement.api.user.EntitlementUserApiException;
 import com.ning.billing.junction.api.Blockable;
 import com.ning.billing.overdue.config.api.BillingState;
 import com.ning.billing.overdue.config.api.OverdueError;
 
 public interface OverdueUserApi {
 
-    public <T extends Blockable> OverdueState<T> refreshOverdueStateFor(T overdueable) throws OverdueError, CatalogApiException;
+    public <T extends Blockable> OverdueState<T> refreshOverdueStateFor(T overdueable) throws OverdueError, CatalogApiException, EntitlementUserApiException;
 
     public <T extends Blockable> void setOverrideBillingStateForAccount(T overdueable, BillingState<T> state) throws OverdueError;
 
diff --git a/api/src/main/java/com/ning/billing/payment/api/PaymentApi.java b/api/src/main/java/com/ning/billing/payment/api/PaymentApi.java
index f73f2a9..b1bef23 100644
--- a/api/src/main/java/com/ning/billing/payment/api/PaymentApi.java
+++ b/api/src/main/java/com/ning/billing/payment/api/PaymentApi.java
@@ -26,37 +26,57 @@ import com.ning.billing.util.callcontext.CallContext;
 
 public interface PaymentApi {
 
-    Either<PaymentErrorEvent, Void> updatePaymentGateway(String accountKey, CallContext context);
+    public void updatePaymentGateway(final String accountKey, final CallContext context)
+        throws PaymentApiException;
 
-    Either<PaymentErrorEvent, PaymentMethodInfo> getPaymentMethod(@Nullable String accountKey, String paymentMethodId);
+    public PaymentMethodInfo getPaymentMethod(final String accountKey, final String paymentMethodId)
+        throws PaymentApiException;
 
-    Either<PaymentErrorEvent, List<PaymentMethodInfo>> getPaymentMethods(String accountKey);
+    public List<PaymentMethodInfo> getPaymentMethods(final String accountKey)
+        throws PaymentApiException;
 
-    Either<PaymentErrorEvent, String> addPaymentMethod(@Nullable String accountKey, PaymentMethodInfo paymentMethod, CallContext context);
+    public String addPaymentMethod(final String accountKey, final PaymentMethodInfo paymentMethod, final CallContext context)
+        throws PaymentApiException;
 
-    Either<PaymentErrorEvent, PaymentMethodInfo> updatePaymentMethod(String accountKey, PaymentMethodInfo paymentMethodInfo, CallContext context);
+    public PaymentMethodInfo updatePaymentMethod(final String accountKey, final PaymentMethodInfo paymentMethodInfo, final CallContext context)
+        throws PaymentApiException;
 
-    Either<PaymentErrorEvent, Void> deletePaymentMethod(String accountKey, String paymentMethodId, CallContext context);
+    public void deletePaymentMethod(final String accountKey, final String paymentMethodId, final CallContext context)
+        throws PaymentApiException;
 
-    List<Either<PaymentErrorEvent, PaymentInfoEvent>> createPayment(String accountKey, List<String> invoiceIds, CallContext context);
-    List<Either<PaymentErrorEvent, PaymentInfoEvent>> createPayment(Account account, List<String> invoiceIds, CallContext context);
-    Either<PaymentErrorEvent, PaymentInfoEvent> createPaymentForPaymentAttempt(UUID paymentAttemptId, CallContext context);
+    public List<PaymentInfoEvent> createPayment(final String accountKey, final List<String> invoiceIds, final CallContext context)
+        throws PaymentApiException;
+    
+    public List<PaymentInfoEvent> createPayment(final Account account, final List<String> invoiceIds, final CallContext context)
+        throws PaymentApiException;
+    
+    public PaymentInfoEvent createPaymentForPaymentAttempt(final UUID paymentAttemptId, final CallContext context)
+        throws PaymentApiException;
 
-    List<Either<PaymentErrorEvent, PaymentInfoEvent>> createRefund(Account account, List<String> invoiceIds, CallContext context); //TODO
+    public List<PaymentInfoEvent> createRefund(final Account account, final List<String> invoiceIds, final CallContext context)
+        throws PaymentApiException;
 
-    Either<PaymentErrorEvent, PaymentProviderAccount> getPaymentProviderAccount(String accountKey);
+    public PaymentProviderAccount getPaymentProviderAccount(final String accountKey)
+        throws PaymentApiException;
 
-    Either<PaymentErrorEvent, String> createPaymentProviderAccount(Account account, CallContext context);
+    public String createPaymentProviderAccount(final Account account, final CallContext context)
+        throws PaymentApiException;
 
-    Either<PaymentErrorEvent, Void> updatePaymentProviderAccountContact(String accountKey, CallContext context);
+    public void updatePaymentProviderAccountContact(String accountKey, CallContext context)
+        throws PaymentApiException;
 
-    PaymentAttempt getPaymentAttemptForPaymentId(String id);
+    public PaymentAttempt getPaymentAttemptForPaymentId(final UUID id)
+        throws PaymentApiException;
 
-    List<PaymentInfoEvent> getPaymentInfoList(List<String> invoiceIds);
-    PaymentInfoEvent getLastPaymentInfo(List<String> invoiceIds);
+    public List<PaymentInfoEvent> getPaymentInfoList(final List<String> invoiceIds)
+        throws PaymentApiException;
 
-    List<PaymentAttempt> getPaymentAttemptsForInvoiceId(String invoiceId);
+    public PaymentInfoEvent getLastPaymentInfo(final List<String> invoiceIds)
+        throws PaymentApiException;
 
-    PaymentInfoEvent getPaymentInfoForPaymentAttemptId(String paymentAttemptId);
+    public List<PaymentAttempt> getPaymentAttemptsForInvoiceId(final String invoiceId)
+        throws PaymentApiException;
 
+    public PaymentInfoEvent getPaymentInfoForPaymentAttemptId(final String paymentAttemptId)
+        throws PaymentApiException;
 }
diff --git a/api/src/main/java/com/ning/billing/payment/api/PaymentApiException.java b/api/src/main/java/com/ning/billing/payment/api/PaymentApiException.java
new file mode 100644
index 0000000..ff81b16
--- /dev/null
+++ b/api/src/main/java/com/ning/billing/payment/api/PaymentApiException.java
@@ -0,0 +1,49 @@
+/* 
+ * 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.payment.api;
+
+import com.ning.billing.BillingExceptionBase;
+import com.ning.billing.ErrorCode;
+import com.ning.billing.account.api.AccountApiException;
+import com.ning.billing.catalog.api.CatalogApiException;
+
+public class PaymentApiException extends BillingExceptionBase {
+    
+    private static final long serialVersionUID = 39445033L;
+
+
+    public PaymentApiException(AccountApiException e) {
+        super(e, e.getCode(), e.getMessage());
+    }
+
+    /*
+    public PaymentApiException(CatalogApiException e) {
+        super(e, e.getCode(), e.getMessage());
+    }
+    */
+    
+    public PaymentApiException(Throwable e, ErrorCode code, Object...args) {
+        super(e, code, args);
+    }
+
+    public PaymentApiException(Throwable e, int code, String message) {
+        super(e, code, message);
+    }
+
+    public PaymentApiException(ErrorCode code, Object...args) {
+        super(code, args);
+    }
+}
diff --git a/api/src/main/java/com/ning/billing/payment/api/PaymentAttempt.java b/api/src/main/java/com/ning/billing/payment/api/PaymentAttempt.java
index 6dca30c..a424fc6 100644
--- a/api/src/main/java/com/ning/billing/payment/api/PaymentAttempt.java
+++ b/api/src/main/java/com/ning/billing/payment/api/PaymentAttempt.java
@@ -26,7 +26,7 @@ import java.util.UUID;
 public interface PaymentAttempt extends Entity {
     DateTime getInvoiceDate();
 
-    String getPaymentId();
+    UUID getPaymentId();
 
     DateTime getPaymentAttemptDate();
 
@@ -39,4 +39,8 @@ public interface PaymentAttempt extends Entity {
     Currency getCurrency();
 
     Integer getRetryCount();
+
+    DateTime getCreatedDate();
+
+    DateTime getUpdatedDate();
 }
diff --git a/api/src/main/java/com/ning/billing/payment/api/PaymentInfoEvent.java b/api/src/main/java/com/ning/billing/payment/api/PaymentInfoEvent.java
index c3b1850..c4cabb9 100644
--- a/api/src/main/java/com/ning/billing/payment/api/PaymentInfoEvent.java
+++ b/api/src/main/java/com/ning/billing/payment/api/PaymentInfoEvent.java
@@ -23,9 +23,6 @@ import org.joda.time.DateTime;
 import com.ning.billing.util.bus.BusEvent;
 
 public interface PaymentInfoEvent extends Entity, BusEvent {
-
-    public String getPaymentId();
-
     public BigDecimal getAmount();
 
     public String getBankIdentificationNumber();
@@ -49,5 +46,4 @@ public interface PaymentInfoEvent extends Entity, BusEvent {
     public String getStatus();
 
     public String getType();
-
 }
diff --git a/api/src/main/java/com/ning/billing/util/dao/ObjectType.java b/api/src/main/java/com/ning/billing/util/dao/ObjectType.java
index f1aa318..18d987c 100644
--- a/api/src/main/java/com/ning/billing/util/dao/ObjectType.java
+++ b/api/src/main/java/com/ning/billing/util/dao/ObjectType.java
@@ -21,6 +21,7 @@ public enum ObjectType {
     ACCOUNT_EMAIL("account email"),
     BUNDLE("subscription bundle"),
     INVOICE("invoice"),
+    RECURRING_INVOICE_ITEM("recurring_invoice_item"),
     SUBSCRIPTION("subscription");
 
     private final String objectName;
diff --git a/api/src/main/java/com/ning/billing/util/queue/QueueLifecycle.java b/api/src/main/java/com/ning/billing/util/queue/QueueLifecycle.java
new file mode 100644
index 0000000..8db4ec7
--- /dev/null
+++ b/api/src/main/java/com/ning/billing/util/queue/QueueLifecycle.java
@@ -0,0 +1,34 @@
+/* 
+ * 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.util.queue;
+
+public interface QueueLifecycle {
+    /**
+     * Starts the queue
+     */
+    public void startQueue();
+
+    /**
+     * Stop the queue
+     *
+     */
+    public void stopQueue();
+    
+    /**
+     *  Processes event from queue
+     */
+    public int doProcessEvents();
+}

beatrix/pom.xml 29(+29 -0)

diff --git a/beatrix/pom.xml b/beatrix/pom.xml
index bed20c1..0e5dc26 100644
--- a/beatrix/pom.xml
+++ b/beatrix/pom.xml
@@ -65,6 +65,13 @@
             <groupId>joda-time</groupId>
             <artifactId>joda-time</artifactId>
         </dependency>
+
+        <!-- TEST SCOPE -->
+        <dependency> 
+            <groupId>com.jayway.awaitility</groupId>
+            <artifactId>awaitility</artifactId>
+            <scope>test</scope>
+        </dependency>      
          <dependency>
             <groupId>com.ning.billing</groupId>
             <artifactId>killbill-junction</artifactId>
@@ -78,6 +85,28 @@
             <scope>test</scope>
         </dependency>
         <dependency>
+            <groupId>com.ning.billing</groupId>
+            <artifactId>killbill-invoice</artifactId>
+            <type>test-jar</type>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>com.ning.billing</groupId>
+            <artifactId>killbill-account</artifactId>
+            <type>test-jar</type>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>com.ning.billing</groupId>
+            <artifactId>killbill-account</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>com.ning.billing</groupId>
+            <artifactId>killbill-overdue</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
             <groupId>org.jdbi</groupId>
             <artifactId>jdbi</artifactId>
             <scope>test</scope>
diff --git a/beatrix/src/test/java/com/ning/billing/beatrix/integration/MockModule.java b/beatrix/src/test/java/com/ning/billing/beatrix/integration/MockModule.java
index 8eafa8d..ea75433 100644
--- a/beatrix/src/test/java/com/ning/billing/beatrix/integration/MockModule.java
+++ b/beatrix/src/test/java/com/ning/billing/beatrix/integration/MockModule.java
@@ -22,8 +22,7 @@ import java.io.IOException;
 import java.net.URL;
 import java.util.Set;
 
-import com.ning.billing.util.email.EmailConfig;
-import com.ning.billing.util.email.EmailModule;
+import com.ning.billing.account.api.AccountService;
 import org.skife.config.ConfigurationObjectFactory;
 import org.skife.jdbi.v2.IDBI;
 
@@ -41,10 +40,10 @@ import com.ning.billing.dbi.DBIProvider;
 import com.ning.billing.dbi.DbiConfig;
 import com.ning.billing.dbi.MysqlTestingHelper;
 import com.ning.billing.entitlement.api.EntitlementService;
-import com.ning.billing.entitlement.glue.EntitlementModule;
+import com.ning.billing.entitlement.glue.DefaultEntitlementModule;
 import com.ning.billing.invoice.api.InvoiceService;
-import com.ning.billing.invoice.glue.InvoiceModule;
-import com.ning.billing.junction.glue.JunctionModule;
+import com.ning.billing.invoice.glue.DefaultInvoiceModule;
+import com.ning.billing.junction.glue.DefaultJunctionModule;
 import com.ning.billing.lifecycle.KillbillService;
 import com.ning.billing.payment.api.PaymentService;
 import com.ning.billing.payment.provider.MockPaymentProviderPluginModule;
@@ -52,6 +51,8 @@ import com.ning.billing.payment.setup.PaymentModule;
 import com.ning.billing.util.bus.BusService;
 import com.ning.billing.util.clock.Clock;
 import com.ning.billing.util.clock.ClockMock;
+import com.ning.billing.util.email.EmailModule;
+import com.ning.billing.util.email.templates.TemplateModule;
 import com.ning.billing.util.glue.BusModule;
 import com.ning.billing.util.glue.CallContextModule;
 import com.ning.billing.util.glue.FieldStoreModule;
@@ -61,8 +62,6 @@ import com.ning.billing.util.glue.TagStoreModule;
 
 
 public class MockModule extends AbstractModule {
-
-
     public static final String PLUGIN_NAME = "yoyo";
 
     @Override
@@ -94,10 +93,11 @@ public class MockModule extends AbstractModule {
         install(new FieldStoreModule());
         install(new AccountModule());
         install(new CatalogModule());
-        install(new EntitlementModule());
-        install(new InvoiceModule());
+        install(new DefaultEntitlementModule());
+        install(new DefaultInvoiceModule());
+        install(new TemplateModule());
         install(new PaymentMockModule());
-        install(new JunctionModule());
+        install(new DefaultJunctionModule());
     }
 
     private static final class PaymentMockModule extends PaymentModule {
@@ -127,6 +127,7 @@ public class MockModule extends AbstractModule {
         @Override
         protected Set<? extends KillbillService> findServices() {
             ImmutableSet<? extends KillbillService> services = new ImmutableSet.Builder<KillbillService>()
+                            .add(injector.getInstance(AccountService.class))
                             .add(injector.getInstance(BusService.class))
                             .add(injector.getInstance(CatalogService.class))
                             .add(injector.getInstance(EntitlementService.class))
diff --git a/beatrix/src/test/java/com/ning/billing/beatrix/integration/overdue/TestOverdueIntegration.java b/beatrix/src/test/java/com/ning/billing/beatrix/integration/overdue/TestOverdueIntegration.java
new file mode 100644
index 0000000..2dfe2ee
--- /dev/null
+++ b/beatrix/src/test/java/com/ning/billing/beatrix/integration/overdue/TestOverdueIntegration.java
@@ -0,0 +1,140 @@
+/*
+ * 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.beatrix.integration.overdue;
+//
+//import static org.testng.Assert.assertNotNull;
+//
+//import java.io.ByteArrayInputStream;
+//import java.io.InputStream;
+//
+//import org.joda.time.DateTime;
+//import org.joda.time.Interval;
+//
+//import com.google.inject.Inject;
+//import com.ning.billing.account.api.Account;
+//import com.ning.billing.beatrix.integration.TestIntegrationBase;
+//import com.ning.billing.catalog.api.BillingPeriod;
+//import com.ning.billing.catalog.api.Duration;
+//import com.ning.billing.catalog.api.PlanPhaseSpecifier;
+//import com.ning.billing.catalog.api.PriceListSet;
+//import com.ning.billing.catalog.api.ProductCategory;
+//import com.ning.billing.entitlement.api.user.SubscriptionBundle;
+//import com.ning.billing.entitlement.api.user.SubscriptionData;
+//import com.ning.billing.junction.api.BlockingApi;
+//import com.ning.billing.overdue.config.OverdueConfig;
+//import com.ning.billing.payment.provider.MockPaymentProviderPlugin;
+//import com.ning.billing.util.clock.ClockMock;
+//import com.ning.billing.util.config.XMLLoader;
+//
+//public class TestOverdueIntegration extends TestIntegrationBase {
+//    private final String configXml =  
+//            "<overdueConfig>" +
+//                    "   <bundleOverdueStates>" +
+//                    "       <state name=\"OD1\">" +
+//                    "           <condition>" +
+//                    "               <timeSinceEarliestUnpaidInvoiceEqualsOrExceeds>" +
+//                    "                   <unit>MONTHS</unit><number>1</number>" +
+//                    "               </timeSinceEarliestUnpaidInvoiceEqualsOrExceeds>" +
+//                    "           </condition>" +
+//                    "           <externalMessage>Reached OD1</externalMessage>" +
+//                    "           <blockChanges>true</blockChanges>" +
+//                    "           <disableEntitlementAndChangesBlocked>false</disableEntitlementAndChangesBlocked>" +
+//                    "       </state>" +
+//                    "       <state name=\"OD2\">" +
+//                    "           <condition>" +
+//                    "               <timeSinceEarliestUnpaidInvoiceEqualsOrExceeds>" +
+//                    "                   <unit>MONTHS</unit><number>2</number>" +
+//                    "               </timeSinceEarliestUnpaidInvoiceEqualsOrExceeds>" +
+//                    "           </condition>" +
+//                    "           <externalMessage>Reached OD1</externalMessage>" +
+//                    "           <blockChanges>true</blockChanges>" +
+//                    "           <disableEntitlementAndChangesBlocked>true</disableEntitlementAndChangesBlocked>" +
+//                    "       </state>" +
+//                    "   </bundleOverdueStates>" +
+//                    "</overdueConfig>";
+//    private OverdueConfig config; 
+//    
+//    @Inject
+//    private ClockMock clock;
+//    
+//    @Inject
+//    private MockPaymentProviderPlugin paymentPlugin;
+//    
+//    @Inject
+//    private BlockingApi blockingApi;
+//    
+//    private Account account;
+//    private SubscriptionBundle bundle;
+//    private String productName;
+//    private BillingPeriod term;
+//    private String planSetName;
+//
+//    long twoWeeks = new Interval(clock.getUTCNow(), clock.getUTCNow().plusWeeks(2)).toDurationMillis();
+//    long fourWeeks = new Interval(clock.getUTCNow(), clock.getUTCNow().plusWeeks(4)).toDurationMillis();
+//    
+//    //@BeforeMethod
+//    public void setup() throws Exception {
+//        InputStream is = new ByteArrayInputStream(configXml.getBytes());
+//        config = XMLLoader.getObjectFromStreamNoValidation(is,  OverdueConfig.class);
+//        Account account = accountUserApi.createAccount(getAccountData(25), null, null, context);
+//        assertNotNull(account);
+//
+//        bundle = entitlementUserApi.createBundleForAccount(account.getId(), "whatever", context);
+//
+//        productName = "Shotgun";
+//        term = BillingPeriod.MONTHLY;
+//        planSetName = PriceListSet.DEFAULT_PRICELIST_NAME;
+//        
+//        // create account
+//        // set mock payments to fail
+//        // reset clock
+//        // configure basic OD state rules for 2 states OD1 1-2month, OD2 2-3 month
+//    }
+//    
+//    //@AfterMethod
+//    public void cleanup(){
+//        // Clear databases
+//    }
+//    
+//    public void testBasicOverdueState() throws Exception {
+//        DateTime initialDate = new DateTime(2012, 4, 25, 0, 3, 42, 0);
+//        clock.setDeltaFromReality(initialDate.getMillis() - clock.getUTCNow().getMillis());
+//        
+//        
+//        // set next invoice to fail and create network 
+//        paymentPlugin.makeNextInvoiceFail();
+//        SubscriptionData baseSubscription = subscriptionDataFromSubscription(entitlementUserApi.createSubscription(bundle.getId(),
+//                new PlanPhaseSpecifier(productName, ProductCategory.BASE, term, planSetName, null), null, context));
+//        assertNotNull(baseSubscription);
+//
+//
+//       // advance time 2weeks
+//        clock.addDeltaFromReality(twoWeeks);
+//        
+//       // should still be in clear state
+//       blockingApi.getBlockingStateFor(bundle);
+//        
+//       // set next invoice to fail and advance time 1 month
+//       clock.addDeltaFromReality(fourWeeks);
+//       
+//       // should now be in OD1 state
+//       // set next invoice to fail and advance time 1 month
+//       // should now be in OD2 state
+//
+//        
+//    }
+//}
diff --git a/beatrix/src/test/java/com/ning/billing/beatrix/integration/payment/PaymentTestModule.java b/beatrix/src/test/java/com/ning/billing/beatrix/integration/payment/PaymentTestModule.java
new file mode 100644
index 0000000..d444776
--- /dev/null
+++ b/beatrix/src/test/java/com/ning/billing/beatrix/integration/payment/PaymentTestModule.java
@@ -0,0 +1,74 @@
+/*
+ * 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.beatrix.integration.payment;
+
+import com.ning.billing.account.dao.MockAccountDao;
+import com.ning.billing.invoice.dao.MockInvoiceDao;
+import org.apache.commons.collections.MapUtils;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.inject.Provider;
+import com.ning.billing.account.dao.AccountDao;
+import com.ning.billing.config.PaymentConfig;
+import com.ning.billing.invoice.dao.InvoiceDao;
+import com.ning.billing.junction.api.BillingApi;
+import com.ning.billing.mock.BrainDeadProxyFactory;
+import com.ning.billing.payment.dao.MockPaymentDao;
+import com.ning.billing.payment.dao.PaymentDao;
+import com.ning.billing.payment.provider.MockPaymentProviderPluginModule;
+import com.ning.billing.payment.setup.PaymentModule;
+import com.ning.billing.util.bus.Bus;
+import com.ning.billing.util.bus.InMemoryBus;
+import com.ning.billing.util.notificationq.MockNotificationQueueService;
+import com.ning.billing.util.notificationq.NotificationQueueService;
+
+public class PaymentTestModule extends PaymentModule {
+	public static class MockProvider implements Provider<BillingApi> {
+		@Override
+		public BillingApi get() {
+			return BrainDeadProxyFactory.createBrainDeadProxyFor(BillingApi.class);
+		}
+
+	}
+
+    public PaymentTestModule() {
+        super(MapUtils.toProperties(ImmutableMap.of("killbill.payment.provider.default", "my-mock",
+                "killbill.payment.engine.events.off", "false")));
+    }
+
+    @Override
+    protected void installPaymentDao() {
+        bind(PaymentDao.class).to(MockPaymentDao.class).asEagerSingleton();
+    }
+
+    @Override
+    protected void installPaymentProviderPlugins(PaymentConfig config) {
+        install(new MockPaymentProviderPluginModule("my-mock"));
+    }
+
+    @Override
+    protected void configure() {
+        super.configure();
+        bind(Bus.class).to(InMemoryBus.class).asEagerSingleton();
+        bind(MockAccountDao.class).asEagerSingleton();
+        bind(AccountDao.class).to(MockAccountDao.class);
+        bind(MockInvoiceDao.class).asEagerSingleton();
+        bind(InvoiceDao.class).to(MockInvoiceDao.class);
+        bind(NotificationQueueService.class).to(MockNotificationQueueService.class).asEagerSingleton();
+
+    }
+}
diff --git a/beatrix/src/test/java/com/ning/billing/beatrix/integration/payment/TestHelper.java b/beatrix/src/test/java/com/ning/billing/beatrix/integration/payment/TestHelper.java
new file mode 100644
index 0000000..631a7dc
--- /dev/null
+++ b/beatrix/src/test/java/com/ning/billing/beatrix/integration/payment/TestHelper.java
@@ -0,0 +1,128 @@
+/*
+ * 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.beatrix.integration.payment;
+
+import java.math.BigDecimal;
+import java.util.UUID;
+
+import com.ning.billing.mock.BrainDeadProxyFactory;
+import org.apache.commons.lang.RandomStringUtils;
+import org.joda.time.DateTime;
+import org.joda.time.DateTimeZone;
+
+import com.google.inject.Inject;
+import com.ning.billing.account.api.Account;
+import com.ning.billing.account.api.AccountUserApi;
+import com.ning.billing.catalog.api.Currency;
+import com.ning.billing.invoice.api.Invoice;
+import com.ning.billing.invoice.api.InvoiceItem;
+import com.ning.billing.invoice.api.test.InvoiceTestApi;
+import com.ning.billing.invoice.model.DefaultInvoice;
+import com.ning.billing.invoice.model.RecurringInvoiceItem;
+import com.ning.billing.mock.BrainDeadProxyFactory.ZombieControl;
+import com.ning.billing.util.callcontext.CallContext;
+import com.ning.billing.util.callcontext.CallContextFactory;
+import com.ning.billing.util.callcontext.CallOrigin;
+import com.ning.billing.util.callcontext.UserType;
+import com.ning.billing.util.entity.EntityPersistenceException;
+
+public class TestHelper {
+    protected final AccountUserApi accountUserApi;
+    protected final InvoiceTestApi invoiceTestApi;
+    private final CallContext context;
+
+    @Inject
+    public TestHelper(CallContextFactory factory, AccountUserApi accountUserApi, InvoiceTestApi invoiceTestApi) {
+        this.accountUserApi = accountUserApi;
+        this.invoiceTestApi = invoiceTestApi;
+        context = factory.createCallContext("Payment Test", CallOrigin.TEST, UserType.TEST);
+    }
+
+    // These helper methods can be overridden in a plugin implementation
+    public Account createTestCreditCardAccount() throws EntityPersistenceException {
+        String email = "ccuser" + RandomStringUtils.randomAlphanumeric(8) + "@example.com";
+        final Account account = createTestAccount(email);
+
+        ((ZombieControl)accountUserApi).addResult("getAccountById", account);
+        ((ZombieControl)accountUserApi).addResult("getAccountByKey", account);
+        return account;
+    }
+
+    public Account createTestPayPalAccount() throws EntityPersistenceException {
+        final Account account = createTestAccount("ppuser@example.com");
+        ((ZombieControl)accountUserApi).addResult("getAccountById", account);
+        ((ZombieControl)accountUserApi).addResult("getAccountByKey", account);
+        return account;
+    }
+
+    private Account createTestAccount(String email) {
+        Account account = BrainDeadProxyFactory.createBrainDeadProxyFor(Account.class);
+        ZombieControl zombie = (ZombieControl) account;
+        zombie.addResult("getId", UUID.randomUUID());
+        String name = "First" + RandomStringUtils.randomAlphanumeric(5) + " " + "Last" + RandomStringUtils.randomAlphanumeric(5);
+        zombie.addResult("getName", name);
+        zombie.addResult("getFirstNameLength", 10);
+        String externalKey = RandomStringUtils.randomAlphanumeric(10);
+        zombie.addResult("getExternalKey", externalKey);
+        zombie.addResult("getPhone", "123-456-7890");
+        zombie.addResult("getEmail", email);
+        zombie.addResult("getCurrency", Currency.USD);
+        zombie.addResult("getBillCycleDay", 1);
+
+        return account;
+    }
+
+    public Invoice createTestInvoice(Account account,
+                                     DateTime targetDate,
+                                     Currency currency,
+                                     InvoiceItem... items) {
+        Invoice invoice = new DefaultInvoice(account.getId(), new DateTime(), targetDate, currency);
+
+        for (InvoiceItem item : items) {
+            if (item instanceof RecurringInvoiceItem) {
+                RecurringInvoiceItem recurringInvoiceItem = (RecurringInvoiceItem) item;
+                invoice.addInvoiceItem(new RecurringInvoiceItem(invoice.getId(),
+                                                               account.getId(),
+                                                               recurringInvoiceItem.getBundleId(),
+                                                               recurringInvoiceItem.getSubscriptionId(),
+                                                               recurringInvoiceItem.getPlanName(),
+                                                               recurringInvoiceItem.getPhaseName(),
+                                                               recurringInvoiceItem.getStartDate(),
+                                                               recurringInvoiceItem.getEndDate(),
+                                                               recurringInvoiceItem.getAmount(),
+                                                               recurringInvoiceItem.getRate(),
+                                                               recurringInvoiceItem.getCurrency()));
+            }
+        }
+
+        invoiceTestApi.create(invoice, context);
+        return invoice;
+    }
+
+    public Invoice createTestInvoice(Account account) {
+        final DateTime now = new DateTime(DateTimeZone.UTC);
+        final UUID subscriptionId = UUID.randomUUID();
+        final UUID bundleId = UUID.randomUUID();
+        final BigDecimal amount = new BigDecimal("10.00");
+        
+        final InvoiceItem item = new RecurringInvoiceItem(null, account.getId(), bundleId, subscriptionId, "test plan", "test phase", now, now.plusMonths(1),
+                amount, new BigDecimal("1.0"), Currency.USD);
+
+
+        return createTestInvoice(account, now, Currency.USD, item);
+    }
+}
diff --git a/beatrix/src/test/java/com/ning/billing/beatrix/integration/TestIntegration.java b/beatrix/src/test/java/com/ning/billing/beatrix/integration/TestIntegration.java
index 3e8dfb4..7996141 100644
--- a/beatrix/src/test/java/com/ning/billing/beatrix/integration/TestIntegration.java
+++ b/beatrix/src/test/java/com/ning/billing/beatrix/integration/TestIntegration.java
@@ -32,7 +32,7 @@ import org.testng.annotations.Test;
 import com.ning.billing.account.api.Account;
 import com.ning.billing.account.api.AccountApiException;
 import com.ning.billing.account.api.AccountData;
-import com.ning.billing.beatrix.integration.TestBusHandler.NextEvent;
+import com.ning.billing.api.TestApiListener.NextEvent;
 import com.ning.billing.catalog.api.BillingPeriod;
 import com.ning.billing.catalog.api.Currency;
 import com.ning.billing.catalog.api.PhaseType;
@@ -49,32 +49,32 @@ import com.ning.billing.invoice.api.Invoice;
 public class TestIntegration extends TestIntegrationBase {
     @Test(groups = "slow", enabled = true)
     public void testBasePlanCompleteWithBillingDayInPast() throws Exception {
+        log.info("Starting testBasePlanCompleteWithBillingDayInPast");
         DateTime startDate = new DateTime(2012, 2, 1, 0, 3, 42, 0);
         testBasePlanComplete(startDate, 31, false);
     }
 
     @Test(groups = "slow", enabled = true)
     public void testBasePlanCompleteWithBillingDayPresent() throws Exception {
+        log.info("Starting testBasePlanCompleteWithBillingDayPresent");
         DateTime startDate = new DateTime(2012, 2, 1, 0, 3, 42, 0);
         testBasePlanComplete(startDate, 1, false);
     }
 
     @Test(groups = "slow", enabled = true)
     public void testBasePlanCompleteWithBillingDayAlignedWithTrial() throws Exception {
+        log.info("Starting testBasePlanCompleteWithBillingDayAlignedWithTrial");
         DateTime startDate = new DateTime(2012, 2, 1, 0, 3, 42, 0);
         testBasePlanComplete(startDate, 2, false);
     }
 
     @Test(groups = "slow", enabled = true)
     public void testBasePlanCompleteWithBillingDayInFuture() throws Exception {
+        log.info("Starting testBasePlanCompleteWithBillingDayInFuture");
         DateTime startDate = new DateTime(2012, 2, 1, 0, 3, 42, 0);
         testBasePlanComplete(startDate, 3, true);
     }
 
-//    private void waitForDebug() throws Exception {
-//        Thread.sleep(600000);
-//    }
-
     @Test(groups = {"slow", "stress"}, enabled = false)
     public void stressTest() throws Exception {
         final int maxIterations = 7;
@@ -95,9 +95,12 @@ public class TestIntegration extends TestIntegrationBase {
         }
     }
 
-    @Test(groups = "slow", enabled = true)
+    // STEPH set to disabled until test written properly and fixed
+    @Test(groups = "slow", enabled = false)
     public void testRepairChangeBPWithAddonIncluded() throws Exception {
         
+        log.info("Starting testRepairChangeBPWithAddonIncluded");
+        
         DateTime initialDate = new DateTime(2012, 4, 25, 0, 3, 42, 0);
         clock.setDeltaFromReality(initialDate.getMillis() - clock.getUTCNow().getMillis());
         
@@ -146,10 +149,13 @@ public class TestIntegration extends TestIntegrationBase {
         busHandler.pushExpectedEvent(NextEvent.PAYMENT);
         clock.addDeltaFromReality(it.toDurationMillis());
         assertTrue(busHandler.isCompleted(DELAY));
+        
+        assertListenerStatus();
     }
    
     @Test(groups = {"slow"})
     public void testRepairForInvoicing() throws AccountApiException, EntitlementUserApiException {
+
         log.info("Starting testRepairForInvoicing");
 
         Account account = accountUserApi.createAccount(getAccountData(1), null, null, context);
@@ -179,9 +185,11 @@ public class TestIntegration extends TestIntegrationBase {
         // TODO: Jeff implement repair
     }
 
-    @Test(groups = "slow", enabled = false)
+    @Test(groups = "slow", enabled = true)
     public void testWithRecreatePlan() throws Exception {
 
+        log.info("Starting testWithRecreatePlan");
+
         DateTime initialDate = new DateTime(2012, 2, 1, 0, 3, 42, 0);
         int billingDay = 2;
 
@@ -242,13 +250,13 @@ public class TestIntegration extends TestIntegrationBase {
         term = BillingPeriod.MONTHLY;
         planSetName = PriceListSet.DEFAULT_PRICELIST_NAME;
 
-        busHandler.pushExpectedEvent(NextEvent.CREATE);
+        busHandler.pushExpectedEvent(NextEvent.RE_CREATE);
         busHandler.pushExpectedEvent(NextEvent.INVOICE);
         busHandler.pushExpectedEvent(NextEvent.PAYMENT);
         subscription.recreate(new PlanPhaseSpecifier(productName, ProductCategory.BASE, term, planSetName, null), endDate, context);
         assertTrue(busHandler.isCompleted(DELAY));
 
-
+        assertListenerStatus();
     }
      
     private void testBasePlanComplete(DateTime initialCreationDate, int billingDay,
@@ -444,12 +452,18 @@ public class TestIntegration extends TestIntegrationBase {
 
         // The invoice system is still working to verify there is nothing to do
         Thread.sleep(DELAY);
+        
+        assertListenerStatus();
+        
         log.info("TEST PASSED !");
     }
 
 
     @Test(groups = "slow")
     public void testForMultipleRecurringPhases() throws AccountApiException, EntitlementUserApiException, InterruptedException {
+
+        log.info("Starting testForMultipleRecurringPhases");
+        
         DateTime initialCreationDate = new DateTime(2012, 2, 1, 0, 3, 42, 0);
         clock.setDeltaFromReality(initialCreationDate.getMillis() - clock.getUTCNow().getMillis());
 
@@ -509,5 +523,7 @@ public class TestIntegration extends TestIntegrationBase {
         invoices = invoiceUserApi.getInvoicesByAccount(accountId);
         assertNotNull(invoices);
         assertEquals(invoices.size(),14);
+
+        assertListenerStatus();
     }
 }
diff --git a/beatrix/src/test/java/com/ning/billing/beatrix/integration/TestIntegrationBase.java b/beatrix/src/test/java/com/ning/billing/beatrix/integration/TestIntegrationBase.java
index a70bce9..579f47c 100644
--- a/beatrix/src/test/java/com/ning/billing/beatrix/integration/TestIntegrationBase.java
+++ b/beatrix/src/test/java/com/ning/billing/beatrix/integration/TestIntegrationBase.java
@@ -36,6 +36,7 @@ import org.skife.jdbi.v2.TransactionCallback;
 import org.skife.jdbi.v2.TransactionStatus;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
+import org.testng.Assert;
 import org.testng.annotations.AfterClass;
 import org.testng.annotations.AfterMethod;
 import org.testng.annotations.BeforeClass;
@@ -45,7 +46,10 @@ import com.google.inject.Inject;
 import com.ning.billing.account.api.AccountData;
 import com.ning.billing.account.api.AccountService;
 import com.ning.billing.account.api.AccountUserApi;
+import com.ning.billing.api.TestApiListener;
+import com.ning.billing.api.TestListenerStatus;
 import com.ning.billing.beatrix.lifecycle.Lifecycle;
+import com.ning.billing.catalog.DefaultCatalogService;
 import com.ning.billing.catalog.api.Currency;
 import com.ning.billing.dbi.MysqlTestingHelper;
 import com.ning.billing.entitlement.api.EntitlementService;
@@ -54,6 +58,7 @@ import com.ning.billing.entitlement.api.user.EntitlementUserApi;
 import com.ning.billing.entitlement.api.user.EntitlementUserApiException;
 import com.ning.billing.entitlement.api.user.Subscription;
 import com.ning.billing.entitlement.api.user.SubscriptionData;
+import com.ning.billing.entitlement.engine.core.Engine;
 import com.ning.billing.invoice.api.Invoice;
 import com.ning.billing.invoice.api.InvoiceItem;
 import com.ning.billing.invoice.api.InvoiceService;
@@ -67,7 +72,7 @@ import com.ning.billing.util.callcontext.DefaultCallContextFactory;
 import com.ning.billing.util.callcontext.UserType;
 import com.ning.billing.util.clock.ClockMock;
 
-public class TestIntegrationBase implements TestFailure {
+public class TestIntegrationBase implements TestListenerStatus {
 
     protected static final int NUMBER_OF_DECIMALS = InvoicingConfiguration.getNumberOfDecimals();
     protected static final int ROUNDING_METHOD = InvoicingConfiguration.getRoundingMode();
@@ -80,7 +85,7 @@ public class TestIntegrationBase implements TestFailure {
     protected static final Logger log = LoggerFactory.getLogger(TestIntegration.class);
     protected static long AT_LEAST_ONE_MONTH_MS =  31L * 24L * 3600L * 1000L;
 
-    protected static final long DELAY = 5000;
+    protected static final long DELAY = 10000;
 
     @Inject
     protected IDBI dbi;
@@ -119,28 +124,29 @@ public class TestIntegrationBase implements TestFailure {
     @Inject
     protected AccountUserApi accountUserApi;
 
-    protected TestBusHandler busHandler;
+    protected TestApiListener busHandler;
 
     
-    private boolean currentTestStatusSuccess;
-    private String currentTestFailedMsg;
+    private boolean isListenerFailed;
+    private String listenerFailedMsg;
     
     @Override
     public void failed(String msg) {
-        currentTestStatusSuccess = false;
-        currentTestFailedMsg = msg;
+        isListenerFailed = true;
+        listenerFailedMsg = msg;
     }
 
     @Override
-    public void reset() {
-        currentTestStatusSuccess = true;
-        currentTestFailedMsg = null;
+    public void resetTestListenerStatus() {
+        isListenerFailed = false;
+        listenerFailedMsg = null;
     }
 
-    protected void assertFailureFromBusHandler() {
-        if (!currentTestStatusSuccess) {
-            log.error(currentTestFailedMsg);
-            fail();
+    
+    protected void assertListenerStatus() {
+        if (isListenerFailed) {
+            log.error(listenerFailedMsg);
+            Assert.fail(listenerFailedMsg);
         }
     }
 
@@ -163,73 +169,50 @@ public class TestIntegrationBase implements TestFailure {
         helper.initDb(junctionDb);
     }
 
+  
     @BeforeClass(groups = "slow")
     public void setup() throws Exception{
 
         setupMySQL();
         
-        cleanupData();
-        
         context = new DefaultCallContextFactory(clock).createCallContext("Integration Test", CallOrigin.TEST, UserType.TEST);
-
-        /**
-         * Initialize lifecyle for subset of services
-         */
-        busHandler = new TestBusHandler(this);
-        lifecycle.fireStartupSequencePriorEventRegistration();
-        busService.getBus().register(busHandler);
-        lifecycle.fireStartupSequencePostEventRegistration();
+        busHandler = new TestApiListener(this);
+        
     }
 
     @AfterClass(groups = "slow")
     public void tearDown() throws Exception {
-        lifecycle.fireShutdownSequencePriorEventUnRegistration();
-        busService.getBus().unregister(busHandler);
-        lifecycle.fireShutdownSequencePostEventUnRegistration();
         helper.stopMysql();
     }
 
 
     @BeforeMethod(groups = "slow")
-    public void setupTest() {
+    public void setupTest() throws Exception {
 
         log.warn("\n");
         log.warn("RESET TEST FRAMEWORK\n\n");
-        cleanupData();
-        busHandler.reset();
+        
+        // Pre test cleanup
+        helper.cleanupAllTables();
+
         clock.resetDeltaFromReality();
-        reset();
+        resetTestListenerStatus();
+        
+        // Start services
+        lifecycle.fireStartupSequencePriorEventRegistration();
+        busService.getBus().register(busHandler);
+        lifecycle.fireStartupSequencePostEventRegistration();
     }
 
     @AfterMethod(groups = "slow")
-    public void cleanupTest() {
+    public void cleanupTest() throws Exception {
+        lifecycle.fireShutdownSequencePriorEventUnRegistration();
+        busService.getBus().unregister(busHandler);
+        lifecycle.fireShutdownSequencePostEventUnRegistration();
+
         log.warn("DONE WITH TEST\n");
     }
     
-    protected void cleanupData() {
-        dbi.inTransaction(new TransactionCallback<Void>() {
-            @Override
-            public Void inTransaction(Handle h, TransactionStatus status)
-                    throws Exception {
-                h.execute("truncate table accounts");
-                h.execute("truncate table entitlement_events");
-                h.execute("truncate table subscriptions");
-                h.execute("truncate table bundles");
-                h.execute("truncate table notifications");
-                h.execute("truncate table claimed_notifications");
-                h.execute("truncate table invoices");
-                h.execute("truncate table fixed_invoice_items");
-                h.execute("truncate table recurring_invoice_items");
-                h.execute("truncate table tag_definitions");
-                h.execute("truncate table tags");
-                h.execute("truncate table custom_fields");
-                h.execute("truncate table invoice_payments");
-                h.execute("truncate table payment_attempts");
-                h.execute("truncate table payments");
-                return null;
-            }
-        });
-    }
 
     protected void verifyTestResult(UUID accountId, UUID subscriptionId,
                                   DateTime startDate, DateTime endDate,
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 6490640..2203f41 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
@@ -32,7 +32,7 @@ import org.testng.annotations.Guice;
 import org.testng.annotations.Test;
 
 import com.ning.billing.account.api.Account;
-import com.ning.billing.beatrix.integration.TestBusHandler.NextEvent;
+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.PlanPhaseSpecifier;
@@ -54,13 +54,15 @@ import com.ning.billing.entitlement.api.user.Subscription.SubscriptionState;
 public class TestRepairIntegration extends TestIntegrationBase {
 
     
-    @Test(groups={"slow"}, enabled=true)
+    @Test(groups={"slow"}, enabled=false)
     public void testRepairChangeBPWithAddonIncludedIntrial() throws Exception {
+        log.info("Starting testRepairChangeBPWithAddonIncludedIntrial");
         testRepairChangeBPWithAddonIncluded(true);
     }
     
-    @Test(groups={"slow"}, enabled=true)
+    @Test(groups={"slow"}, enabled=false)
     public void testRepairChangeBPWithAddonIncludedOutOfTrial() throws Exception {
+        log.info("Starting testRepairChangeBPWithAddonIncludedOutOfTrial");
         testRepairChangeBPWithAddonIncluded(false);
     }
     
@@ -168,7 +170,7 @@ public class TestRepairIntegration extends TestIntegrationBase {
             assertEquals(newBaseSubscription.getAllTransitions().size(), 3);
             assertEquals(newBaseSubscription.getActiveVersion(), SubscriptionEvents.INITIAL_VERSION + 1);
 
-            assertFailureFromBusHandler();
+            assertListenerStatus();
         }
      }
     
@@ -327,6 +329,4 @@ public class TestRepairIntegration extends TestIntegrationBase {
             }
         });
     }
-    
-
 }
diff --git a/bin/clean-and-install b/bin/clean-and-install
new file mode 100755
index 0000000..c6c3ac4
--- /dev/null
+++ b/bin/clean-and-install
@@ -0,0 +1,22 @@
+#! /usr/bin/env bash
+
+###################################################################################
+#                                                                                 #
+#                   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.                                                         #
+#                                                                                 #
+###################################################################################
+
+bin/db-helper -a clean -d killbill; 
+mvn -Dcom.ning.billing.dbi.test.useLocalDb=true clean install 

entitlement/pom.xml 54(+24 -30)

diff --git a/entitlement/pom.xml b/entitlement/pom.xml
index 426d9f8..172287f 100644
--- a/entitlement/pom.xml
+++ b/entitlement/pom.xml
@@ -39,23 +39,6 @@
         </dependency>
         <dependency>
             <groupId>com.ning.billing</groupId>
-            <artifactId>killbill-catalog</artifactId>
-            <type>test-jar</type>
-            <scope>test</scope>
-        </dependency>
-        <dependency>
-            <groupId>com.ning.billing</groupId>
-            <artifactId>killbill-catalog</artifactId>
-            <scope>test</scope>
-        </dependency>
-         <dependency>
-            <groupId>com.ning.billing</groupId>
-            <artifactId>killbill-util</artifactId>
-            <type>test-jar</type>
-            <scope>test</scope>
-        </dependency>
-        <dependency>
-            <groupId>com.ning.billing</groupId>
             <artifactId>killbill-util</artifactId>
         </dependency>
         <dependency>
@@ -63,14 +46,7 @@
             <artifactId>commons-io</artifactId>
             <scope>test</scope>
         </dependency>
-        <!-- Same here, this is really debatable whether or not we should keep that here -->
-        <dependency>
-            <groupId>com.ning.billing</groupId>
-            <artifactId>killbill-account</artifactId>
-            <type>test-jar</type>
-            <scope>test</scope>
-        </dependency>
-        <dependency>
+       <dependency>
             <groupId>joda-time</groupId>
             <artifactId>joda-time</artifactId>
         </dependency>
@@ -87,11 +63,6 @@
             <artifactId>slf4j-log4j12</artifactId>
         </dependency>
         <dependency>
-            <groupId>org.testng</groupId>
-            <artifactId>testng</artifactId>
-            <scope>test</scope>
-        </dependency>
-        <dependency>
             <groupId>org.codehaus.jackson</groupId>
             <artifactId>jackson-core-asl</artifactId>
         </dependency>
@@ -108,11 +79,34 @@
             <artifactId>stringtemplate</artifactId>
             <scope>runtime</scope>
         </dependency>
+        <!-- TEST SCOPE -->
         <dependency>
             <groupId>mysql</groupId>
             <artifactId>mysql-connector-mxj</artifactId>
             <scope>test</scope>
         </dependency>
+                <dependency>
+            <groupId>com.ning.billing</groupId>
+            <artifactId>killbill-catalog</artifactId>
+            <type>test-jar</type>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>com.ning.billing</groupId>
+            <artifactId>killbill-catalog</artifactId>
+            <scope>test</scope>
+        </dependency>
+         <dependency>
+            <groupId>com.ning.billing</groupId>
+            <artifactId>killbill-util</artifactId>
+            <type>test-jar</type>
+            <scope>test</scope>
+       </dependency> 
+         <dependency>
+            <groupId>org.testng</groupId>
+            <artifactId>testng</artifactId>
+            <scope>test</scope>
+        </dependency>
         <dependency>
             <groupId>mysql</groupId>
             <artifactId>mysql-connector-mxj-db-files</artifactId>
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 ae3cde0..0e44528 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
@@ -21,7 +21,6 @@ import com.ning.billing.ErrorCode;
 import com.ning.billing.catalog.api.*;
 import com.ning.billing.entitlement.api.user.EntitlementUserApiException;
 import com.ning.billing.entitlement.api.user.SubscriptionData;
-import com.ning.billing.entitlement.api.user.SubscriptionEvent;
 import com.ning.billing.entitlement.api.user.SubscriptionTransitionData;
 import com.ning.billing.entitlement.exceptions.EntitlementError;
 import com.ning.billing.util.clock.DefaultClock;
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/api/billing/DefaultChargeThruApi.java b/entitlement/src/main/java/com/ning/billing/entitlement/api/billing/DefaultChargeThruApi.java
index f47ac5e..21b97f8 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/api/billing/DefaultChargeThruApi.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/api/billing/DefaultChargeThruApi.java
@@ -18,29 +18,19 @@ package com.ning.billing.entitlement.api.billing;
 
 import java.util.UUID;
 
-import com.ning.billing.entitlement.api.user.Subscription;
-import com.ning.billing.util.dao.EntityAudit;
-import com.ning.billing.util.dao.TableName;
 import org.joda.time.DateTime;
-import org.skife.jdbi.v2.sqlobject.mixins.Transmogrifier;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
 
 import com.google.inject.Inject;
 import com.ning.billing.entitlement.api.SubscriptionFactory;
 import com.ning.billing.entitlement.api.user.DefaultSubscriptionFactory.SubscriptionBuilder;
 import com.ning.billing.entitlement.api.user.SubscriptionData;
 import com.ning.billing.entitlement.engine.dao.EntitlementDao;
-import com.ning.billing.entitlement.engine.dao.SubscriptionSqlDao;
-import com.ning.billing.util.ChangeType;
 import com.ning.billing.util.callcontext.CallContext;
 
 public class DefaultChargeThruApi implements ChargeThruApi {
-	private static final Logger log = LoggerFactory.getLogger(DefaultChargeThruApi.class);
-
     private final EntitlementDao entitlementDao;
     private final SubscriptionFactory subscriptionFactory;
-
+  
     @Inject
     public DefaultChargeThruApi(final SubscriptionFactory subscriptionFactory, final EntitlementDao dao) {
         super();
@@ -62,25 +52,4 @@ public class DefaultChargeThruApi implements ChargeThruApi {
             .setPaidThroughDate(subscription.getPaidThroughDate());
         entitlementDao.updateChargedThroughDate(new SubscriptionData(builder), context);
     }
-
-    @Override
-    public void setChargedThroughDateFromTransaction(final Transmogrifier transactionalDao, final UUID subscriptionId,
-                                                     final DateTime ctd, final CallContext context) {
-        SubscriptionSqlDao subscriptionSqlDao = transactionalDao.become(SubscriptionSqlDao.class);
-        SubscriptionData subscription = (SubscriptionData) subscriptionSqlDao.getSubscriptionFromId(subscriptionId.toString());
-
-        if (subscription == null) {
-            log.warn("Subscription not found when setting CTD.");
-        } else {
-            DateTime chargedThroughDate = subscription.getChargedThroughDate();
-            if (chargedThroughDate == null || chargedThroughDate.isBefore(ctd)) {
-                subscriptionSqlDao.updateChargedThroughDate(subscriptionId.toString(),
-                        ctd.toDate(), context);
-
-                Long recordId = subscriptionSqlDao.getRecordId(TableName.SUBSCRIPTIONS, subscriptionId.toString());
-                EntityAudit audit = new EntityAudit(recordId, ChangeType.UPDATE);
-                subscriptionSqlDao.insertAuditFromTransaction(TableName.SUBSCRIPTIONS, audit, context);
-            }
-        }
-    }
 }
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/api/SubscriptionApiService.java b/entitlement/src/main/java/com/ning/billing/entitlement/api/SubscriptionApiService.java
index 046b982..5b0f192 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/api/SubscriptionApiService.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/api/SubscriptionApiService.java
@@ -15,6 +15,8 @@
  */
 package com.ning.billing.entitlement.api;
 
+import java.util.List;
+
 import org.joda.time.DateTime;
 
 import com.ning.billing.catalog.api.BillingPeriod;
@@ -23,6 +25,7 @@ import com.ning.billing.catalog.api.Plan;
 import com.ning.billing.catalog.api.PlanPhaseSpecifier;
 import com.ning.billing.entitlement.api.user.EntitlementUserApiException;
 import com.ning.billing.entitlement.api.user.SubscriptionData;
+import com.ning.billing.entitlement.api.user.SubscriptionStatusDryRun;
 import com.ning.billing.entitlement.api.user.DefaultSubscriptionFactory.SubscriptionBuilder;
 import com.ning.billing.util.callcontext.CallContext;
 
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 9cee03c..cf9db7c 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
@@ -18,11 +18,9 @@ package com.ning.billing.entitlement.api.timeline;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.Comparator;
-import java.util.HashMap;
 import java.util.Iterator;
 import java.util.LinkedList;
 import java.util.List;
-import java.util.Map;
 import java.util.Set;
 import java.util.TreeSet;
 import java.util.UUID;
@@ -34,28 +32,19 @@ import com.google.inject.name.Named;
 import com.ning.billing.ErrorCode;
 import com.ning.billing.catalog.api.CatalogApiException;
 import com.ning.billing.catalog.api.CatalogService;
-import com.ning.billing.catalog.api.CatalogUserApi;
 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.timeline.BundleTimeline;
-import com.ning.billing.entitlement.api.timeline.EntitlementTimelineApi;
-import com.ning.billing.entitlement.api.timeline.EntitlementRepairException;
-import com.ning.billing.entitlement.api.timeline.SubscriptionTimeline;
-import com.ning.billing.entitlement.api.timeline.SubscriptionTimeline.ExistingEvent;
 import com.ning.billing.entitlement.api.timeline.SubscriptionTimeline.NewEvent;
 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.DefaultSubscriptionFactory.SubscriptionBuilder;
-import com.ning.billing.entitlement.api.user.SubscriptionEvent;
 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.events.EntitlementEvent.EventType;
-import com.ning.billing.entitlement.exceptions.EntitlementError;
-import com.ning.billing.entitlement.glue.EntitlementModule;
+import com.ning.billing.entitlement.glue.DefaultEntitlementModule;
 import com.ning.billing.util.callcontext.CallContext;
 
 public class DefaultEntitlementTimelineApi implements EntitlementTimelineApi {
@@ -74,8 +63,8 @@ public class DefaultEntitlementTimelineApi implements EntitlementTimelineApi {
 
 
     @Inject
-    public DefaultEntitlementTimelineApi(@Named(EntitlementModule.REPAIR_NAMED) final SubscriptionFactory factory, final CatalogService catalogService,
-            @Named(EntitlementModule.REPAIR_NAMED) final RepairEntitlementLifecycleDao repairDao, final EntitlementDao dao) {
+    public DefaultEntitlementTimelineApi(@Named(DefaultEntitlementModule.REPAIR_NAMED) final SubscriptionFactory factory, final CatalogService catalogService,
+            @Named(DefaultEntitlementModule.REPAIR_NAMED) final RepairEntitlementLifecycleDao repairDao, final EntitlementDao dao) {
         this.catalogService = catalogService;
         this.dao = dao;
         this.repairDao = repairDao;
@@ -350,7 +339,7 @@ public class DefaultEntitlementTimelineApi implements EntitlementTimelineApi {
             if (firstBPDeletedTime != null && 
                     ! cur.getEffectiveDate().isBefore(firstBPDeletedTime) &&
                     ! foundDeletedEvent) {
-                throw new EntitlementRepairException(ErrorCode.ENT_REPAIR_MISSING_AO_DELETE_EVENT, cur.getId(), data.getId());                
+                throw new EntitlementRepairException(ErrorCode.ENT_REPAIR_MISSING_AO_DELETE_EVENT, cur.getId(), data.getId());
             }
 
             if (nbDeleted == 0) {
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 315550d..4080326 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
@@ -32,7 +32,6 @@ import com.ning.billing.catalog.api.Plan;
 import com.ning.billing.catalog.api.PlanPhaseSpecifier;
 import com.ning.billing.catalog.api.ProductCategory;
 import com.ning.billing.entitlement.api.SubscriptionTransitionType;
-import com.ning.billing.entitlement.api.timeline.SubscriptionTimeline;
 import com.ning.billing.entitlement.api.user.SubscriptionTransitionData;
 import com.ning.billing.entitlement.events.EntitlementEvent;
 import com.ning.billing.entitlement.events.phase.PhaseEvent;
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/api/timeline/RepairSubscriptionApiService.java b/entitlement/src/main/java/com/ning/billing/entitlement/api/timeline/RepairSubscriptionApiService.java
index 659b3be..dcc0c95 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/api/timeline/RepairSubscriptionApiService.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/api/timeline/RepairSubscriptionApiService.java
@@ -23,13 +23,13 @@ import com.ning.billing.entitlement.alignment.PlanAligner;
 import com.ning.billing.entitlement.api.SubscriptionApiService;
 import com.ning.billing.entitlement.api.user.DefaultSubscriptionApiService;
 import com.ning.billing.entitlement.engine.dao.EntitlementDao;
-import com.ning.billing.entitlement.glue.EntitlementModule;
+import com.ning.billing.entitlement.glue.DefaultEntitlementModule;
 import com.ning.billing.util.clock.Clock;
 
 public class RepairSubscriptionApiService extends DefaultSubscriptionApiService implements SubscriptionApiService {
 
     @Inject
-    public RepairSubscriptionApiService(Clock clock, @Named(EntitlementModule.REPAIR_NAMED) EntitlementDao dao,
+    public RepairSubscriptionApiService(Clock clock, @Named(DefaultEntitlementModule.REPAIR_NAMED) EntitlementDao dao,
             CatalogService catalogService, PlanAligner planAligner) {
         super(clock, dao, catalogService, planAligner);
     }
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/api/timeline/RepairSubscriptionFactory.java b/entitlement/src/main/java/com/ning/billing/entitlement/api/timeline/RepairSubscriptionFactory.java
index e63c3a4..61933c3 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/api/timeline/RepairSubscriptionFactory.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/api/timeline/RepairSubscriptionFactory.java
@@ -29,7 +29,7 @@ import com.ning.billing.entitlement.api.user.DefaultSubscriptionFactory.Subscrip
 import com.ning.billing.entitlement.engine.addon.AddonUtils;
 import com.ning.billing.entitlement.engine.dao.EntitlementDao;
 import com.ning.billing.entitlement.events.EntitlementEvent;
-import com.ning.billing.entitlement.glue.EntitlementModule;
+import com.ning.billing.entitlement.glue.DefaultEntitlementModule;
 import com.ning.billing.util.clock.Clock;
 
 public class RepairSubscriptionFactory extends DefaultSubscriptionFactory implements SubscriptionFactory {
@@ -38,8 +38,8 @@ public class RepairSubscriptionFactory extends DefaultSubscriptionFactory implem
     private final EntitlementDao repairDao;
     
     @Inject
-    public RepairSubscriptionFactory(@Named(EntitlementModule.REPAIR_NAMED) SubscriptionApiService apiService,
-            @Named(EntitlementModule.REPAIR_NAMED) EntitlementDao dao,
+    public RepairSubscriptionFactory(@Named(DefaultEntitlementModule.REPAIR_NAMED) SubscriptionApiService apiService,
+            @Named(DefaultEntitlementModule.REPAIR_NAMED) EntitlementDao dao,
             Clock clock, CatalogService catalogService, AddonUtils addonUtils) {
         super(apiService, clock, catalogService);
         this.addonUtils = addonUtils;
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/api/timeline/SubscriptionDataRepair.java b/entitlement/src/main/java/com/ning/billing/entitlement/api/timeline/SubscriptionDataRepair.java
index 75574d3..7f526d4 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/api/timeline/SubscriptionDataRepair.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/api/timeline/SubscriptionDataRepair.java
@@ -17,11 +17,9 @@ package com.ning.billing.entitlement.api.timeline;
 
 import java.util.Collection;
 import java.util.Iterator;
-import java.util.LinkedList;
 import java.util.List;
 
 import org.joda.time.DateTime;
-import org.omg.CORBA.Request;
 
 import com.google.common.base.Predicate;
 import com.google.common.collect.Collections2;
@@ -35,10 +33,8 @@ import com.ning.billing.catalog.api.Product;
 import com.ning.billing.catalog.api.ProductCategory;
 import com.ning.billing.entitlement.api.SubscriptionApiService;
 import com.ning.billing.entitlement.api.SubscriptionTransitionType;
-import com.ning.billing.entitlement.api.timeline.EntitlementRepairException;
 import com.ning.billing.entitlement.api.user.EntitlementUserApiException;
 import com.ning.billing.entitlement.api.user.SubscriptionData;
-import com.ning.billing.entitlement.api.user.SubscriptionEvent;
 import com.ning.billing.entitlement.api.user.DefaultSubscriptionFactory.SubscriptionBuilder;
 import com.ning.billing.entitlement.api.user.SubscriptionTransitionData;
 import com.ning.billing.entitlement.engine.addon.AddonUtils;
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 115c60b..7d92852 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
@@ -16,6 +16,7 @@
 
 package com.ning.billing.entitlement.api.user;
 
+import java.util.LinkedList;
 import java.util.List;
 import java.util.UUID;
 
@@ -30,10 +31,11 @@ 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.PriceListSet;
-import com.ning.billing.catalog.api.Product;
+import com.ning.billing.catalog.api.ProductCategory;
 import com.ning.billing.entitlement.api.SubscriptionFactory;
 import com.ning.billing.entitlement.api.user.Subscription.SubscriptionState;
 import com.ning.billing.entitlement.api.user.DefaultSubscriptionFactory.SubscriptionBuilder;
+import com.ning.billing.entitlement.api.user.SubscriptionStatusDryRun.DryRunChangeReason;
 import com.ning.billing.entitlement.engine.addon.AddonUtils;
 import com.ning.billing.entitlement.engine.dao.EntitlementDao;
 import com.ning.billing.entitlement.exceptions.EntitlementError;
@@ -61,19 +63,32 @@ public class DefaultEntitlementUserApi implements EntitlementUserApi {
         this.subscriptionFactory = subscriptionFactory;
     }
 
+    
     @Override
-    public SubscriptionBundle getBundleFromId(UUID id) {
-        return dao.getSubscriptionBundleFromId(id);
+    public SubscriptionBundle getBundleFromId(UUID id) throws EntitlementUserApiException {
+        SubscriptionBundle result = dao.getSubscriptionBundleFromId(id);
+        if (result == null) {
+            throw new EntitlementUserApiException(ErrorCode.ENT_GET_INVALID_BUNDLE_ID, id.toString());
+        }
+        return result;
     }
 
     @Override
-    public Subscription getSubscriptionFromId(UUID id) {
-        return dao.getSubscriptionFromId(subscriptionFactory, id);
+    public Subscription getSubscriptionFromId(UUID id) throws EntitlementUserApiException {
+        Subscription result = dao.getSubscriptionFromId(subscriptionFactory, id);
+        if (result == null) {
+            throw new EntitlementUserApiException(ErrorCode.ENT_INVALID_SUBSCRIPTION_ID, id);
+        }
+        return result;
     }
 
     @Override
-    public SubscriptionBundle getBundleForKey(String bundleKey) {
-        return dao.getSubscriptionBundleFromKey(bundleKey);
+    public SubscriptionBundle getBundleForKey(String bundleKey) throws EntitlementUserApiException {
+        SubscriptionBundle result =  dao.getSubscriptionBundleFromKey(bundleKey);
+        if (result == null) {
+            throw new EntitlementUserApiException(ErrorCode.ENT_GET_INVALID_BUNDLE_KEY, bundleKey);
+        }
+        return result;
     }
 
     @Override
@@ -92,8 +107,12 @@ public class DefaultEntitlementUserApi implements EntitlementUserApi {
     }
 
     @Override
-    public Subscription getBaseSubscription(UUID bundleId) {
-        return dao.getBaseSubscription(subscriptionFactory, bundleId);
+    public Subscription getBaseSubscription(UUID bundleId) throws EntitlementUserApiException {
+        Subscription result =  dao.getBaseSubscription(subscriptionFactory, bundleId);
+        if (result == null) {
+            throw new EntitlementUserApiException(ErrorCode.ENT_GET_NO_SUCH_BASE_SUBSCRIPTION, bundleId);
+        }
+        return result;
     }
     
 
@@ -197,4 +216,42 @@ public class DefaultEntitlementUserApi implements EntitlementUserApi {
         }
         return result;
     }
+
+
+    @Override
+    public List<SubscriptionStatusDryRun> getDryRunChangePlanStatus(UUID subscriptionId, String baseProductName, DateTime requestedDate)
+            throws EntitlementUserApiException {
+
+        Subscription subscription = dao.getSubscriptionFromId(subscriptionFactory, subscriptionId);
+        if (subscription == null) {
+            throw new EntitlementUserApiException(ErrorCode.ENT_INVALID_SUBSCRIPTION_ID, subscriptionId);
+        }
+        if (subscription.getCategory() != ProductCategory.BASE) {
+            throw new EntitlementUserApiException(ErrorCode.ENT_CHANGE_DRY_RUN_NOT_BP);
+        }
+        
+        List<SubscriptionStatusDryRun> result = new LinkedList<SubscriptionStatusDryRun>();
+        
+        List<Subscription> bundleSubscriptions = dao.getSubscriptions(subscriptionFactory, subscription.getBundleId());
+        for (Subscription cur : bundleSubscriptions) {
+            if (cur.getId().equals(subscriptionId)) {
+                continue;
+            }
+            
+            DryRunChangeReason reason = null;
+            if (addonUtils.isAddonIncludedFromProdName(baseProductName, requestedDate, cur.getCurrentPlan())) {
+                reason = DryRunChangeReason.AO_INCLUDED_IN_NEW_PLAN;
+            } else if (addonUtils.isAddonAvailableFromProdName(baseProductName, requestedDate, cur.getCurrentPlan())) {
+                reason = DryRunChangeReason.AO_AVAILABLE_IN_NEW_PLAN;
+            } else {
+                reason = DryRunChangeReason.AO_NOT_AVAILABLE_IN_NEW_PLAN;
+            }
+            SubscriptionStatusDryRun status = new DefaultSubscriptionStatusDryRun(cur.getId(), 
+                    cur.getCurrentPlan().getProduct().getName(), cur.getCurrentPhase().getPhaseType(),
+                    cur.getCurrentPlan().getBillingPeriod(),
+                    cur.getCurrentPriceList().getName(), reason);
+            result.add(status);
+        }
+        return result;
+    }
 }
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 ed2cb05..44ed086 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
@@ -229,7 +229,7 @@ public class DefaultSubscriptionApiService implements SubscriptionApiService {
                 subscription.rebuildTransitions(dao.getEventsForSubscription(subscription.getId()), catalogService.getFullCatalog());
                 return true;
     }
-
+    
     public boolean changePlan(SubscriptionData subscription, String productName, BillingPeriod term,
             String priceList, DateTime requestedDate, CallContext context)
 
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/api/user/DefaultSubscriptionEvent.java b/entitlement/src/main/java/com/ning/billing/entitlement/api/user/DefaultSubscriptionEvent.java
index ea310f9..3cf74ec 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/api/user/DefaultSubscriptionEvent.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/api/user/DefaultSubscriptionEvent.java
@@ -348,5 +348,24 @@ public class DefaultSubscriptionEvent implements SubscriptionEvent {
             return false;
         return true;
     }
+
+    @Override
+    public String toString() {
+        return "DefaultSubscriptionEvent [transitionType=" + transitionType
+                + ", effectiveTransitionTime=" + effectiveTransitionTime        
+                + ", totalOrdering=" + totalOrdering
+                + ", subscriptionId=" + subscriptionId + ", bundleId="
+                + bundleId + ", eventId=" + eventId
+                + ", requestedTransitionTime=" + requestedTransitionTime
+                + ", previousState=" + previousState + ", previousPriceList="
+                + previousPriceList + ", previousPlan=" + previousPlan
+                + ", previousPhase=" + previousPhase + ", nextState="
+                + nextState + ", nextPriceList=" + nextPriceList
+                + ", nextPlan=" + nextPlan + ", nextPhase=" + nextPhase
+                + ", remainingEventsForUserOperation="
+                + remainingEventsForUserOperation + ", userToken=" + userToken
+                + ", startDate=" + startDate + "]";
+                
+    }
     
 }
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 eb59d38..37a94f6 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
@@ -21,7 +21,6 @@ import com.ning.billing.catalog.api.CatalogService;
 import com.ning.billing.catalog.api.ProductCategory;
 import com.ning.billing.entitlement.api.SubscriptionApiService;
 import com.ning.billing.entitlement.api.SubscriptionFactory;
-import com.ning.billing.entitlement.api.timeline.SubscriptionDataRepair;
 import com.ning.billing.entitlement.events.EntitlementEvent;
 import com.ning.billing.entitlement.exceptions.EntitlementError;
 import com.ning.billing.util.clock.Clock;
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/api/user/DefaultSubscriptionStatusDryRun.java b/entitlement/src/main/java/com/ning/billing/entitlement/api/user/DefaultSubscriptionStatusDryRun.java
new file mode 100644
index 0000000..9971f41
--- /dev/null
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/api/user/DefaultSubscriptionStatusDryRun.java
@@ -0,0 +1,75 @@
+/* 
+ * 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.user;
+
+import java.util.UUID;
+
+import com.ning.billing.catalog.api.BillingPeriod;
+import com.ning.billing.catalog.api.PhaseType;
+
+public class DefaultSubscriptionStatusDryRun implements SubscriptionStatusDryRun {
+        
+    private final UUID id;
+    private final String productName;
+    private final PhaseType phaseType;
+    private final BillingPeriod billingPeriod;
+    private final String priceList;
+    private final DryRunChangeReason reason;
+    
+    
+    public DefaultSubscriptionStatusDryRun(final UUID id, final String productName,
+            final PhaseType phaseType, final BillingPeriod billingPeriod, final String priceList,
+            final DryRunChangeReason reason) {
+        super();
+        this.id = id;
+        this.productName = productName;
+        this.phaseType = phaseType;
+        this.billingPeriod = billingPeriod;
+        this.priceList = priceList;
+        this.reason = reason;
+    }
+
+    @Override
+    public UUID getId() {
+        return id;
+    }
+
+    @Override
+    public String getProductName() {
+        return productName;
+    }
+
+    @Override
+    public PhaseType getPhaseType() {
+        return phaseType;
+    }
+
+    
+    @Override
+    public BillingPeriod getBillingPeriod() {
+        return billingPeriod;
+    }
+
+    @Override
+    public String getPriceList() {
+        return priceList;
+    }
+
+    @Override
+    public DryRunChangeReason getReason() {
+        return reason;
+    }
+}
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 1a3e792..815410a 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
@@ -200,7 +200,6 @@ public class SubscriptionData extends ExtendedEntityBase implements Subscription
         return apiService.recreatePlan(this, spec, requestedDate, context);
     }
 
-
     @Override
     public SubscriptionEvent getPendingTransition() {
         SubscriptionTransitionData data = getPendingTransitionData();
@@ -209,6 +208,11 @@ public class SubscriptionData extends ExtendedEntityBase implements Subscription
         }
         return new DefaultSubscriptionEvent(data, startDate);
     }
+    
+    @Override
+    public BlockingState getBlockingState() {
+        throw new UnsupportedOperationException();
+    }
 
     protected SubscriptionTransitionData getPendingTransitionData() {
         if (transitions == null) {
@@ -520,10 +524,4 @@ public class SubscriptionData extends ExtendedEntityBase implements Subscription
             previousPriceList = nextPriceList;
         }
     }
-
-    @Override
-    public BlockingState getBlockingState() {
-        throw new UnsupportedOperationException();
-    }
-
 }
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/engine/addon/AddonUtils.java b/entitlement/src/main/java/com/ning/billing/entitlement/engine/addon/AddonUtils.java
index 3208f42..2a4a550 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/engine/addon/AddonUtils.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/engine/addon/AddonUtils.java
@@ -63,7 +63,16 @@ public class AddonUtils {
         }
     }
 
-    public boolean isAddonAvailable(final String basePlanName, final DateTime requestedDate, final Plan targetAddOnPlan) {
+    public boolean isAddonAvailableFromProdName(final String baseProductName, final DateTime requestedDate, final Plan targetAddOnPlan) {
+        try {
+            Product product = catalogService.getFullCatalog().findProduct(baseProductName, requestedDate);
+            return isAddonAvailable(product, targetAddOnPlan);
+        } catch (CatalogApiException e) {
+            throw new EntitlementError(e);
+        }
+    }
+
+    public boolean isAddonAvailableFromPlanName(final String basePlanName, final DateTime requestedDate, final Plan targetAddOnPlan) {
         try {
             Plan plan = catalogService.getFullCatalog().findPlan(basePlanName, requestedDate);
             Product product = plan.getProduct();
@@ -84,9 +93,19 @@ public class AddonUtils {
         }
         return false;
     }
+    
+    public boolean isAddonIncludedFromProdName(final String baseProductName, final DateTime requestedDate, final Plan targetAddOnPlan) {
+        try {            
+            Product product = catalogService.getFullCatalog().findProduct(baseProductName, requestedDate);
+            return isAddonIncluded(product, targetAddOnPlan);
+        } catch (CatalogApiException e) {
+            throw new EntitlementError(e);
+        }
 
-    public boolean isAddonIncluded(final String basePlanName,  final DateTime requestedDate, final Plan targetAddOnPlan) {
-        try {
+    }
+
+    public boolean isAddonIncludedFromPlanName(final String basePlanName, final DateTime requestedDate, final Plan targetAddOnPlan) {
+        try {            
             Plan plan = catalogService.getFullCatalog().findPlan(basePlanName, requestedDate);
             Product product = plan.getProduct();
             return isAddonIncluded(product, targetAddOnPlan);
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/engine/core/Engine.java b/entitlement/src/main/java/com/ning/billing/entitlement/engine/core/Engine.java
index c8f82a2..accb829 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/engine/core/Engine.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/engine/core/Engine.java
@@ -30,8 +30,6 @@ import org.slf4j.LoggerFactory;
 
 import com.google.inject.Inject;
 
-import com.google.inject.name.Named;
-
 
 import com.ning.billing.catalog.api.Plan;
 import com.ning.billing.catalog.api.Product;
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 f973ab0..c503d91 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
@@ -129,12 +129,12 @@ public class AuditedEntitlementDao implements EntitlementDao {
     public SubscriptionBundle createSubscriptionBundle(final SubscriptionBundleData bundle, final CallContext context) {
         return bundlesDao.inTransaction(new Transaction<SubscriptionBundle, BundleSqlDao>() {
             @Override
-            public SubscriptionBundle inTransaction(BundleSqlDao bundlesDao, TransactionStatus status) {
+            public SubscriptionBundle inTransaction(BundleSqlDao transactional, TransactionStatus status) {
                 bundlesDao.insertBundle(bundle, context);
-                Long recordId = bundlesDao.getRecordId(TableName.ENTITLEMENT_EVENTS, bundle.getId().toString());
+                Long recordId = bundlesDao.getRecordId(bundle.getId().toString());
 
-                EntityAudit audit = new EntityAudit(recordId, ChangeType.INSERT);
-                bundlesDao.insertAuditFromTransaction(TableName.BUNDLES, audit, context);
+                EntityAudit audit = new EntityAudit(TableName.BUNDLES, recordId, ChangeType.INSERT);
+                bundlesDao.insertAuditFromTransaction(audit, context);
 
                 return bundle;
             }
@@ -198,18 +198,18 @@ public class AuditedEntitlementDao implements EntitlementDao {
             public Void inTransaction(SubscriptionSqlDao transactionalDao,
                     TransactionStatus status) throws Exception {
                 String subscriptionId = subscription.getId().toString();
-                transactionalDao.updateChargedThroughDate(subscriptionId, ctd, context);
-                Long subscriptionRecordId = transactionalDao.getRecordId(TableName.SUBSCRIPTIONS, subscriptionId);
-                EntityAudit subscriptionAudit = new EntityAudit(subscriptionRecordId, ChangeType.UPDATE);
-                transactionalDao.insertAuditFromTransaction(TableName.SUBSCRIPTIONS, subscriptionAudit, context);
+                transactionalDao.updateChargedThroughDate(subscription.getId().toString(), ctd, context);
+                Long subscriptionRecordId = transactionalDao.getRecordId(subscriptionId);
+                EntityAudit subscriptionAudit = new EntityAudit(TableName.SUBSCRIPTIONS, subscriptionRecordId, ChangeType.UPDATE);
+                transactionalDao.insertAuditFromTransaction(subscriptionAudit, context);
 
                 BundleSqlDao bundleSqlDao = transactionalDao.become(BundleSqlDao.class);
                 String bundleId = subscription.getBundleId().toString();
                 bundleSqlDao.updateBundleLastSysTime(bundleId, clock.getUTCNow().toDate());
                 // SubscriptionBundle bundle = bundleSqlDao.getById(bundleId);
-                Long recordId = bundleSqlDao.getRecordId(TableName.ENTITLEMENT_EVENTS, bundleId);
-                EntityAudit bundleAudit = new EntityAudit(recordId, ChangeType.UPDATE);
-                bundleSqlDao.insertAuditFromTransaction(TableName.SUBSCRIPTIONS, bundleAudit, context);
+                Long recordId = bundleSqlDao.getRecordId(bundleId);
+                EntityAudit bundleAudit = new EntityAudit(TableName.BUNDLES, recordId, ChangeType.UPDATE);
+                bundleSqlDao.insertAuditFromTransaction(bundleAudit, context);
                 return null;
             }
         });
@@ -219,15 +219,15 @@ public class AuditedEntitlementDao implements EntitlementDao {
     public void createNextPhaseEvent(final UUID subscriptionId, final EntitlementEvent nextPhase, final CallContext context) {
         eventsDao.inTransaction(new Transaction<Void, EntitlementEventSqlDao>() {
             @Override
-            public Void inTransaction(EntitlementEventSqlDao dao, TransactionStatus status) throws Exception {
-                cancelNextPhaseEventFromTransaction(subscriptionId, dao, context);
-                dao.insertEvent(nextPhase, context);
+            public Void inTransaction(EntitlementEventSqlDao transactional, TransactionStatus status) throws Exception {
+                cancelNextPhaseEventFromTransaction(subscriptionId, transactional, context);
+                transactional.insertEvent(nextPhase, context);
 
-                Long recordId = dao.getRecordId(TableName.ENTITLEMENT_EVENTS, nextPhase.getId().toString());
-                EntityAudit audit = new EntityAudit(recordId, ChangeType.INSERT);
-                dao.insertAuditFromTransaction(TableName.ENTITLEMENT_EVENTS, audit, context);
+                Long recordId = transactional.getRecordId(nextPhase.getId().toString());
+                EntityAudit audit = new EntityAudit(TableName.ENTITLEMENT_EVENTS, recordId, ChangeType.INSERT);
+                transactional.insertAuditFromTransaction(audit, context);
 
-                recordFutureNotificationFromTransaction(dao,
+                recordFutureNotificationFromTransaction(transactional,
                         nextPhase.getEffectiveDate(),
                         new EntitlementNotificationKey(nextPhase.getId()));
                 return null;
@@ -279,28 +279,28 @@ public class AuditedEntitlementDao implements EntitlementDao {
         subscriptionsDao.inTransaction(new Transaction<Void, SubscriptionSqlDao>() {
 
             @Override
-            public Void inTransaction(SubscriptionSqlDao dao,
+            public Void inTransaction(SubscriptionSqlDao transactional,
                     TransactionStatus status) throws Exception {
 
-                dao.insertSubscription(subscription, context);
-                Long subscriptionRecordId = dao.getRecordId(TableName.SUBSCRIPTIONS, subscription.getId().toString());
-                EntityAudit audit = new EntityAudit(subscriptionRecordId, ChangeType.INSERT);
-                dao.insertAuditFromTransaction(TableName.SUBSCRIPTIONS, audit, context);
+                transactional.insertSubscription(subscription, context);
+                Long subscriptionRecordId = transactional.getRecordId(subscription.getId().toString());
+                EntityAudit audit = new EntityAudit(TableName.SUBSCRIPTIONS, subscriptionRecordId, ChangeType.INSERT);
+                transactional.insertAuditFromTransaction(audit, context);
 
                 // STEPH batch as well
-                EntitlementEventSqlDao eventsDaoFromSameTransaction = dao.become(EntitlementEventSqlDao.class);
+                EntitlementEventSqlDao eventsDaoFromSameTransaction = transactional.become(EntitlementEventSqlDao.class);
                 List<EntityAudit> audits = new ArrayList<EntityAudit>();
 
                 for (final EntitlementEvent cur : initialEvents) {
                     eventsDaoFromSameTransaction.insertEvent(cur, context);
-                    Long recordId = eventsDaoFromSameTransaction.getRecordId(TableName.ENTITLEMENT_EVENTS, cur.getId().toString());
-                    audits.add(new EntityAudit(recordId, ChangeType.INSERT));
-                    recordFutureNotificationFromTransaction(dao,
+                    Long recordId = eventsDaoFromSameTransaction.getRecordId(cur.getId().toString());
+                    audits.add(new EntityAudit(TableName.ENTITLEMENT_EVENTS, recordId, ChangeType.INSERT));
+                    recordFutureNotificationFromTransaction(transactional,
                             cur.getEffectiveDate(),
                             new EntitlementNotificationKey(cur.getId()));
                 }
 
-                eventsDaoFromSameTransaction.insertAuditFromTransaction(TableName.ENTITLEMENT_EVENTS, audits, context);
+                eventsDaoFromSameTransaction.insertAuditFromTransaction(audits, context);
                 return null;
             }
         });
@@ -312,21 +312,21 @@ public class AuditedEntitlementDao implements EntitlementDao {
 
         eventsDao.inTransaction(new Transaction<Void, EntitlementEventSqlDao>() {
             @Override
-            public Void inTransaction(EntitlementEventSqlDao dao,
+            public Void inTransaction(EntitlementEventSqlDao transactional,
                     TransactionStatus status) throws Exception {
 
                 List<EntityAudit> audits = new ArrayList<EntityAudit>();
                 for (final EntitlementEvent cur : recreateEvents) {
-                    dao.insertEvent(cur, context);
-                    Long recordId = dao.getRecordId(TableName.ENTITLEMENT_EVENTS, cur.getId().toString());
-                    audits.add(new EntityAudit(recordId, ChangeType.INSERT));
-                    recordFutureNotificationFromTransaction(dao,
+                    transactional.insertEvent(cur, context);
+                    Long recordId = transactional.getRecordId(cur.getId().toString());
+                    audits.add(new EntityAudit(TableName.ENTITLEMENT_EVENTS, recordId, ChangeType.INSERT));
+                    recordFutureNotificationFromTransaction(transactional,
                             cur.getEffectiveDate(),
                             new EntitlementNotificationKey(cur.getId()));
 
                 }
 
-                dao.insertAuditFromTransaction(TableName.ENTITLEMENT_EVENTS, audits, context);
+                transactional.insertAuditFromTransaction(audits, context);
                 return null;
             }
         });
@@ -337,19 +337,19 @@ public class AuditedEntitlementDao implements EntitlementDao {
 
         eventsDao.inTransaction(new Transaction<Void, EntitlementEventSqlDao>() {
             @Override
-            public Void inTransaction(EntitlementEventSqlDao dao,
+            public Void inTransaction(EntitlementEventSqlDao transactional,
                     TransactionStatus status) throws Exception {
-                cancelNextCancelEventFromTransaction(subscriptionId, dao, context);
-                cancelNextChangeEventFromTransaction(subscriptionId, dao, context);
-                cancelNextPhaseEventFromTransaction(subscriptionId, dao, context);
-                dao.insertEvent(cancelEvent, context);
+                cancelNextCancelEventFromTransaction(subscriptionId, transactional, context);
+                cancelNextChangeEventFromTransaction(subscriptionId, transactional, context);
+                cancelNextPhaseEventFromTransaction(subscriptionId, transactional, context);
+                transactional.insertEvent(cancelEvent, context);
                 String cancelEventId = cancelEvent.getId().toString();
 
-                Long recordId = dao.getRecordId(TableName.ENTITLEMENT_EVENTS, cancelEventId);
-                EntityAudit audit = new EntityAudit(recordId, ChangeType.INSERT);
-                dao.insertAuditFromTransaction(TableName.ENTITLEMENT_EVENTS, audit, context);
+                Long recordId = transactional.getRecordId(cancelEventId);
+                EntityAudit audit = new EntityAudit(TableName.ENTITLEMENT_EVENTS, recordId, ChangeType.INSERT);
+                transactional.insertAuditFromTransaction(audit, context);
 
-                recordFutureNotificationFromTransaction(dao,
+                recordFutureNotificationFromTransaction(transactional,
                         cancelEvent.getEffectiveDate(),
                         new EntitlementNotificationKey(cancelEvent.getId(), seqId));
                 return null;
@@ -363,12 +363,12 @@ public class AuditedEntitlementDao implements EntitlementDao {
         eventsDao.inTransaction(new Transaction<Void, EntitlementEventSqlDao>() {
 
             @Override
-            public Void inTransaction(EntitlementEventSqlDao dao,
+            public Void inTransaction(EntitlementEventSqlDao transactional,
                     TransactionStatus status) throws Exception {
 
                 EntitlementEvent cancelledEvent = null;
                 Date now = clock.getUTCNow().toDate();
-                List<EntitlementEvent> events = dao.getFutureActiveEventForSubscription(subscriptionId.toString(), now);
+                List<EntitlementEvent> events = transactional.getFutureActiveEventForSubscription(subscriptionId.toString(), now);
 
                 for (EntitlementEvent cur : events) {
                     if (cur.getType() == EventType.API_USER && ((ApiEvent) cur).getEventType() == ApiEventType.CANCEL) {
@@ -383,20 +383,20 @@ public class AuditedEntitlementDao implements EntitlementDao {
                     List<EntityAudit> eventAudits = new ArrayList<EntityAudit>();
 
                     String cancelledEventId = cancelledEvent.getId().toString();
-                    dao.unactiveEvent(cancelledEventId, context);
-                    Long cancelledRecordId = dao.getRecordId(TableName.ENTITLEMENT_EVENTS, cancelledEventId);
-                    eventAudits.add(new EntityAudit(cancelledRecordId, ChangeType.UPDATE));
+                    transactional.unactiveEvent(cancelledEventId, context);
+                    Long cancelledRecordId = transactional.getRecordId(cancelledEventId);
+                    eventAudits.add(new EntityAudit(TableName.ENTITLEMENT_EVENTS, cancelledRecordId, ChangeType.UPDATE));
 
                     for (final EntitlementEvent cur : uncancelEvents) {
-                        dao.insertEvent(cur, context);
-                        Long recordId = dao.getRecordId(TableName.ENTITLEMENT_EVENTS, cur.getId().toString());
-                        eventAudits.add(new EntityAudit(recordId, ChangeType.INSERT));
-                        recordFutureNotificationFromTransaction(dao,
+                        transactional.insertEvent(cur, context);
+                        Long recordId = transactional.getRecordId(cur.getId().toString());
+                        eventAudits.add(new EntityAudit(TableName.ENTITLEMENT_EVENTS, recordId, ChangeType.INSERT));
+                        recordFutureNotificationFromTransaction(transactional,
                                 cur.getEffectiveDate(),
                                 new EntitlementNotificationKey(cur.getId()));
                     }
 
-                    dao.insertAuditFromTransaction(TableName.ENTITLEMENT_EVENTS, eventAudits, context);
+                    transactional.insertAuditFromTransaction(eventAudits, context);
                 }
                 return null;
             }
@@ -407,22 +407,22 @@ public class AuditedEntitlementDao implements EntitlementDao {
     public void changePlan(final UUID subscriptionId, final List<EntitlementEvent> changeEvents, final CallContext context) {
         eventsDao.inTransaction(new Transaction<Void, EntitlementEventSqlDao>() {
             @Override
-            public Void inTransaction(EntitlementEventSqlDao dao, TransactionStatus status) throws Exception {
-                cancelNextChangeEventFromTransaction(subscriptionId, dao, context);
-                cancelNextPhaseEventFromTransaction(subscriptionId, dao, context);
+            public Void inTransaction(EntitlementEventSqlDao transactional, TransactionStatus status) throws Exception {
+                cancelNextChangeEventFromTransaction(subscriptionId, transactional, context);
+                cancelNextPhaseEventFromTransaction(subscriptionId, transactional, context);
 
                 List<EntityAudit> eventAudits = new ArrayList<EntityAudit>();
                 for (final EntitlementEvent cur : changeEvents) {
-                    dao.insertEvent(cur, context);
-                    Long recordId = dao.getRecordId(TableName.ENTITLEMENT_EVENTS, cur.getId().toString());
-                    eventAudits.add(new EntityAudit(recordId, ChangeType.INSERT));
+                    transactional.insertEvent(cur, context);
+                    Long recordId = transactional.getRecordId(cur.getId().toString());
+                    eventAudits.add(new EntityAudit(TableName.ENTITLEMENT_EVENTS, recordId, ChangeType.INSERT));
 
-                    recordFutureNotificationFromTransaction(dao,
+                    recordFutureNotificationFromTransaction(transactional,
                             cur.getEffectiveDate(),
                             new EntitlementNotificationKey(cur.getId()));
                 }
 
-                dao.insertAuditFromTransaction(TableName.ENTITLEMENT_EVENTS, eventAudits, context);
+                transactional.insertAuditFromTransaction(eventAudits, context);
                 return null;
             }
         });
@@ -462,9 +462,9 @@ public class AuditedEntitlementDao implements EntitlementDao {
         if (futureEvent != null) {
             String eventId = futureEvent.getId().toString();
             dao.unactiveEvent(eventId, context);
-            Long recordId = dao.getRecordId(TableName.ENTITLEMENT_EVENTS, eventId);
-            EntityAudit audit = new EntityAudit(recordId, ChangeType.UPDATE);
-            dao.insertAuditFromTransaction(TableName.ENTITLEMENT_EVENTS, audit, context);
+            Long recordId = dao.getRecordId(eventId);
+            EntityAudit audit = new EntityAudit(TableName.ENTITLEMENT_EVENTS, recordId, ChangeType.UPDATE);
+            dao.insertAuditFromTransaction(audit, context);
         }
     }
 
@@ -536,8 +536,8 @@ public class AuditedEntitlementDao implements EntitlementDao {
 
                         boolean createCancelEvent = (futureBaseEvent != null) &&
                         ((futureBaseEvent instanceof ApiEventCancel) ||
-                                ((! addonUtils.isAddonAvailable(baseProductName, futureBaseEvent.getEffectiveDate(), targetAddOnPlan)) ||
-                                        (addonUtils.isAddonIncluded(baseProductName, futureBaseEvent.getEffectiveDate(), targetAddOnPlan))));
+                                ((! addonUtils.isAddonAvailableFromPlanName(baseProductName, futureBaseEvent.getEffectiveDate(), targetAddOnPlan)) ||
+                                        (addonUtils.isAddonIncludedFromPlanName(baseProductName, futureBaseEvent.getEffectiveDate(), targetAddOnPlan))));
 
                         if (createCancelEvent) {
                             DateTime now = clock.getUTCNow();
@@ -571,15 +571,13 @@ public class AuditedEntitlementDao implements EntitlementDao {
         eventsDao.inTransaction(new Transaction<Void, EntitlementEventSqlDao>() {
 
             @Override
-            public Void inTransaction(EntitlementEventSqlDao transEventDao,
+            public Void inTransaction(EntitlementEventSqlDao transactional,
                     TransactionStatus status) throws Exception {
 
-                SubscriptionSqlDao transSubDao = transEventDao.become(SubscriptionSqlDao.class);
-                BundleSqlDao transBundleDao = transEventDao.become(BundleSqlDao.class);
+                SubscriptionSqlDao transSubDao = transactional.become(SubscriptionSqlDao.class);
+                BundleSqlDao transBundleDao = transactional.become(BundleSqlDao.class);
 
-                List<EntityAudit> bundleAudits = new ArrayList<EntityAudit>();
-                List<EntityAudit> subscriptionAudits = new ArrayList<EntityAudit>();
-                List<EntityAudit> eventAudits = new ArrayList<EntityAudit>();
+                List<EntityAudit> audits = new ArrayList<EntityAudit>();
 
                 Long recordId;
 
@@ -590,27 +588,25 @@ public class AuditedEntitlementDao implements EntitlementDao {
 
                         SubscriptionData subData = curSubscription.getData();
                         for (final EntitlementEvent curEvent : curSubscription.getInitialEvents()) {
-                            transEventDao.insertEvent(curEvent, context);
-                            recordId = transEventDao.getRecordId(TableName.ENTITLEMENT_EVENTS, curEvent.getId().toString());
-                            eventAudits.add(new EntityAudit(recordId, ChangeType.INSERT));
+                            transactional.insertEvent(curEvent, context);
+                            recordId = transactional.getRecordId(curEvent.getId().toString());
+                            audits.add(new EntityAudit(TableName.ENTITLEMENT_EVENTS, recordId, ChangeType.INSERT));
 
-                            recordFutureNotificationFromTransaction(transEventDao,
+                            recordFutureNotificationFromTransaction(transactional,
                                     curEvent.getEffectiveDate(),
                                     new EntitlementNotificationKey(curEvent.getId()));
                         }
                         transSubDao.insertSubscription(subData, context);
-                        recordId = transSubDao.getRecordId(TableName.SUBSCRIPTIONS, subData.getId().toString());
-                        subscriptionAudits.add(new EntityAudit(recordId, ChangeType.INSERT));
+                        recordId = transSubDao.getRecordId(subData.getId().toString());
+                        audits.add(new EntityAudit(TableName.SUBSCRIPTIONS, recordId, ChangeType.INSERT));
                     }
                     transBundleDao.insertBundle(bundleData, context);
-                    recordId = transBundleDao.getRecordId(TableName.BUNDLES, bundleData.getId().toString());
-                    bundleAudits.add(new EntityAudit(recordId, ChangeType.INSERT));
+                    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(TableName.SUBSCRIPTIONS, subscriptionAudits, context);
-                transBundleDao.insertAuditFromTransaction(TableName.BUNDLES, bundleAudits, context);
-                transEventDao.insertAuditFromTransaction(TableName.ENTITLEMENT_EVENTS, eventAudits, context);
+                transSubDao.insertAuditFromTransaction(audits, context);
 
                 return null;
             }
@@ -643,7 +639,7 @@ public class AuditedEntitlementDao implements EntitlementDao {
                     RepairEntitlementEvent busEvent = new DefaultRepairEntitlementEvent(context.getUserToken(), accountId, bundleId, clock.getUTCNow());
                     eventBus.postFromTransaction(busEvent, transactional);
                 } catch (EventBusException e) {
-                    log.warn("Failed to post repair entitlement event for bundle " + bundleId);
+                    log.warn("Failed to post repair entitlement event for bundle " + bundleId, e);
                 }
                 return null;
             }
@@ -675,9 +671,9 @@ public class AuditedEntitlementDao implements EntitlementDao {
     public void saveCustomFields(final SubscriptionData subscription, final CallContext context) {
         subscriptionsDao.inTransaction(new Transaction<Void, SubscriptionSqlDao>() {
             @Override
-            public Void inTransaction(SubscriptionSqlDao transactionalDao,
+            public Void inTransaction(SubscriptionSqlDao transactional,
                     TransactionStatus status) throws Exception {
-                updateCustomFieldsFromTransaction(transactionalDao, subscription, context);
+                updateCustomFieldsFromTransaction(transactional, subscription, context);
                 return null;
             }
         });
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/events/EventBase.java b/entitlement/src/main/java/com/ning/billing/entitlement/events/EventBase.java
index 5861e5d..4afc75d 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/events/EventBase.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/events/EventBase.java
@@ -17,7 +17,6 @@
 package com.ning.billing.entitlement.events;
 
 import com.ning.billing.entitlement.events.user.ApiEvent;
-import com.ning.billing.entitlement.exceptions.EntitlementError;
 import org.joda.time.DateTime;
 
 import java.util.UUID;
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/events/phase/PhaseEventData.java b/entitlement/src/main/java/com/ning/billing/entitlement/events/phase/PhaseEventData.java
index 47546ea..23f7b2b 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/events/phase/PhaseEventData.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/events/phase/PhaseEventData.java
@@ -17,7 +17,6 @@
 package com.ning.billing.entitlement.events.phase;
 
 
-import com.ning.billing.entitlement.alignment.TimedPhase;
 import com.ning.billing.entitlement.api.user.SubscriptionData;
 import com.ning.billing.entitlement.events.EventBase;
 import org.joda.time.DateTime;
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/events/user/ApiEventBase.java b/entitlement/src/main/java/com/ning/billing/entitlement/events/user/ApiEventBase.java
index c68da2b..d2ea706 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/events/user/ApiEventBase.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/events/user/ApiEventBase.java
@@ -17,7 +17,6 @@
 package com.ning.billing.entitlement.events.user;
 
 import com.ning.billing.entitlement.events.EventBase;
-import org.joda.time.DateTime;
 
 import java.util.UUID;
 
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/api/migration/TestMigration.java b/entitlement/src/test/java/com/ning/billing/entitlement/api/migration/TestMigration.java
index 7380ec9..2681a0b 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/api/migration/TestMigration.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/api/migration/TestMigration.java
@@ -27,15 +27,15 @@ import java.util.List;
 import java.util.UUID;
 
 import org.joda.time.DateTime;
+import org.joda.time.Interval;
 import org.testng.Assert;
 
+import com.ning.billing.api.TestApiListener.NextEvent;
 import com.ning.billing.catalog.api.BillingPeriod;
-import com.ning.billing.catalog.api.Duration;
 import com.ning.billing.catalog.api.PhaseType;
 import com.ning.billing.catalog.api.PlanPhaseSpecifier;
 import com.ning.billing.catalog.api.PriceListSet;
 import com.ning.billing.catalog.api.ProductCategory;
-import com.ning.billing.entitlement.api.ApiTestListener.NextEvent;
 import com.ning.billing.entitlement.api.TestApiBase;
 import com.ning.billing.entitlement.api.migration.EntitlementMigrationApi.EntitlementAccountMigration;
 import com.ning.billing.entitlement.api.migration.EntitlementMigrationApi.EntitlementBundleMigration;
@@ -44,20 +44,22 @@ import com.ning.billing.entitlement.api.migration.EntitlementMigrationApi.Entitl
 import com.ning.billing.entitlement.api.user.Subscription;
 import com.ning.billing.entitlement.api.user.Subscription.SubscriptionState;
 import com.ning.billing.entitlement.api.user.SubscriptionBundle;
-import org.testng.annotations.Test;
 
 public abstract class TestMigration extends TestApiBase {
     public void testSingleBasePlan() {
 
         try {
+            
+            log.info("Starting testSingleBasePlan");
+
             final DateTime startDate = clock.getUTCNow().minusMonths(2);
-            DateTime beforeMigration = clock.getUTCNow();
+            DateTime beforeMigration =  clock.getUTCNow();
             EntitlementAccountMigration toBeMigrated = createAccountWithRegularBasePlan(startDate);
             DateTime afterMigration = clock.getUTCNow();
 
-            testListener.pushNextApiExpectedEvent(NextEvent.MIGRATE_ENTITLEMENT);
+            testListener.pushExpectedEvent(NextEvent.MIGRATE_ENTITLEMENT);
             migrationApi.migrate(toBeMigrated, context);
-            assertTrue(testListener.isApiCompleted(5000));
+            assertTrue(testListener.isCompleted(5000));
 
             List<SubscriptionBundle> bundles = entitlementApi.getBundlesForAccount(toBeMigrated.getAccountKey());
             assertEquals(bundles.size(), 1);
@@ -73,6 +75,8 @@ public abstract class TestMigration extends TestApiBase {
             assertEquals(subscription.getState(), SubscriptionState.ACTIVE);
             assertEquals(subscription.getCurrentPlan().getName(), "assault-rifle-annual");
             assertEquals(subscription.getChargedThroughDate(), startDate.plusYears(1));
+
+            assertListenerStatus();
         } catch (EntitlementMigrationApiException e) {
             Assert.fail("", e);
         }
@@ -80,16 +84,17 @@ public abstract class TestMigration extends TestApiBase {
 
     public void testPlanWithAddOn() {
         try {
+            log.info("Starting testPlanWithAddOn");
             DateTime beforeMigration = clock.getUTCNow();
             final DateTime initalBPStart = clock.getUTCNow().minusMonths(3);
             final DateTime initalAddonStart = clock.getUTCNow().minusMonths(1).plusDays(7);
             EntitlementAccountMigration toBeMigrated = createAccountWithRegularBasePlanAndAddons(initalBPStart, initalAddonStart);
             DateTime afterMigration = clock.getUTCNow();
 
-            testListener.pushNextApiExpectedEvent(NextEvent.MIGRATE_ENTITLEMENT);
-            testListener.pushNextApiExpectedEvent(NextEvent.MIGRATE_ENTITLEMENT);
+            testListener.pushExpectedEvent(NextEvent.MIGRATE_ENTITLEMENT);
+            testListener.pushExpectedEvent(NextEvent.MIGRATE_ENTITLEMENT);
             migrationApi.migrate(toBeMigrated, context);
-            assertTrue(testListener.isApiCompleted(5000));
+            assertTrue(testListener.isCompleted(5000));
 
             List<SubscriptionBundle> bundles = entitlementApi.getBundlesForAccount(toBeMigrated.getAccountKey());
             assertEquals(bundles.size(), 1);
@@ -120,6 +125,7 @@ public abstract class TestMigration extends TestApiBase {
             assertEquals(aoSubscription.getCurrentPlan().getName(), "telescopic-scope-monthly");
             assertEquals(aoSubscription.getChargedThroughDate(), initalAddonStart.plusMonths(1));
 
+            assertListenerStatus();
         } catch (EntitlementMigrationApiException e) {
             Assert.fail("", e);
         }
@@ -128,15 +134,15 @@ public abstract class TestMigration extends TestApiBase {
     public void testSingleBasePlanFutureCancelled() {
 
         try {
-
+            log.info("Starting testSingleBasePlanFutureCancelled");
             final DateTime startDate = clock.getUTCNow().minusMonths(1);
             DateTime beforeMigration = clock.getUTCNow();
             EntitlementAccountMigration toBeMigrated = createAccountWithRegularBasePlanFutreCancelled(startDate);
             DateTime afterMigration = clock.getUTCNow();
 
-            testListener.pushNextApiExpectedEvent(NextEvent.MIGRATE_ENTITLEMENT);
+            testListener.pushExpectedEvent(NextEvent.MIGRATE_ENTITLEMENT);
             migrationApi.migrate(toBeMigrated, context);
-            assertTrue(testListener.isApiCompleted(5000));
+            assertTrue(testListener.isCompleted(5000));
 
             List<SubscriptionBundle> bundles = entitlementApi.getBundlesForAccount(toBeMigrated.getAccountKey());
             assertEquals(bundles.size(), 1);
@@ -153,11 +159,13 @@ public abstract class TestMigration extends TestApiBase {
             assertEquals(subscription.getCurrentPlan().getName(), "assault-rifle-annual");
             assertEquals(subscription.getChargedThroughDate(), startDate.plusYears(1));
 
-            testListener.pushNextApiExpectedEvent(NextEvent.MIGRATE_BILLING);
-            testListener.pushNextApiExpectedEvent(NextEvent.CANCEL);
-            Duration oneYear = getDurationYear(1);
-            clock.setDeltaFromReality(oneYear, 0);
-            assertTrue(testListener.isApiCompleted(5000));
+
+            testListener.pushExpectedEvent(NextEvent.MIGRATE_BILLING);
+            testListener.pushExpectedEvent(NextEvent.CANCEL);
+            
+            Interval it = new Interval(clock.getUTCNow(), clock.getUTCNow().plusYears(1));
+            clock.addDeltaFromReality(it.toDurationMillis());
+            assertTrue(testListener.isCompleted(5000));
 
             assertDateWithin(subscription.getStartDate(), beforeMigration, afterMigration);
             assertNotNull(subscription.getEndDate());
@@ -167,6 +175,8 @@ public abstract class TestMigration extends TestApiBase {
             assertEquals(subscription.getState(), SubscriptionState.CANCELLED);
             assertNull(subscription.getCurrentPlan());
 
+            assertListenerStatus();
+            
         } catch (EntitlementMigrationApiException e) {
             Assert.fail("", e);
         }
@@ -175,12 +185,14 @@ public abstract class TestMigration extends TestApiBase {
     public void testSingleBasePlanWithPendingPhase() {
 
         try {
+            
+            log.info("Starting testSingleBasePlanWithPendingPhase");
             final DateTime trialDate = clock.getUTCNow().minusDays(10);
             EntitlementAccountMigration toBeMigrated = createAccountFuturePendingPhase(trialDate);
 
-            testListener.pushNextApiExpectedEvent(NextEvent.MIGRATE_ENTITLEMENT);
+            testListener.pushExpectedEvent(NextEvent.MIGRATE_ENTITLEMENT);
             migrationApi.migrate(toBeMigrated, context);
-            assertTrue(testListener.isApiCompleted(5000));
+            assertTrue(testListener.isCompleted(5000));
 
             List<SubscriptionBundle> bundles = entitlementApi.getBundlesForAccount(toBeMigrated.getAccountKey());
             assertEquals(bundles.size(), 1);
@@ -198,11 +210,12 @@ public abstract class TestMigration extends TestApiBase {
             assertEquals(subscription.getCurrentPlan().getName(), "assault-rifle-monthly");
             assertEquals(subscription.getChargedThroughDate(), trialDate.plusDays(30));
 
-            testListener.pushNextApiExpectedEvent(NextEvent.MIGRATE_BILLING);
-            testListener.pushNextApiExpectedEvent(NextEvent.PHASE);
-            Duration thirtyDays = getDurationDay(30);
-            clock.setDeltaFromReality(thirtyDays, 0);
-            assertTrue(testListener.isApiCompleted(5000));
+            testListener.pushExpectedEvent(NextEvent.MIGRATE_BILLING);
+            testListener.pushExpectedEvent(NextEvent.PHASE);
+
+            Interval it = new Interval(clock.getUTCNow(), clock.getUTCNow().plusDays(30));
+            clock.addDeltaFromReality(it.toDurationMillis());
+            assertTrue(testListener.isCompleted(5000));
 
             assertEquals(subscription.getStartDate(), trialDate);
             assertEquals(subscription.getEndDate(), null);
@@ -212,6 +225,8 @@ public abstract class TestMigration extends TestApiBase {
             assertEquals(subscription.getCurrentPlan().getName(), "assault-rifle-monthly");
             assertEquals(subscription.getCurrentPhase().getName(), "assault-rifle-monthly-evergreen");
 
+            assertListenerStatus();
+            
         } catch (EntitlementMigrationApiException e) {
             Assert.fail("", e);
         }
@@ -220,13 +235,14 @@ public abstract class TestMigration extends TestApiBase {
     public void testSingleBasePlanWithPendingChange() {
 
         try {
+            log.info("Starting testSingleBasePlanWithPendingChange");
             DateTime beforeMigration = clock.getUTCNow();
             EntitlementAccountMigration toBeMigrated = createAccountFuturePendingChange();
             DateTime afterMigration = clock.getUTCNow();
 
-            testListener.pushNextApiExpectedEvent(NextEvent.MIGRATE_ENTITLEMENT);
+            testListener.pushExpectedEvent(NextEvent.MIGRATE_ENTITLEMENT);
             migrationApi.migrate(toBeMigrated, context);
-            assertTrue(testListener.isApiCompleted(5000));
+            assertTrue(testListener.isCompleted(5000));
 
             List<SubscriptionBundle> bundles = entitlementApi.getBundlesForAccount(toBeMigrated.getAccountKey());
             assertEquals(bundles.size(), 1);
@@ -242,10 +258,11 @@ public abstract class TestMigration extends TestApiBase {
             assertEquals(subscription.getState(), SubscriptionState.ACTIVE);
             assertEquals(subscription.getCurrentPlan().getName(), "assault-rifle-monthly");
 
-            testListener.pushNextApiExpectedEvent(NextEvent.CHANGE);
-            Duration oneMonth = getDurationMonth(1);
-            clock.setDeltaFromReality(oneMonth, 0);
-            assertTrue(testListener.isApiCompleted(5000));
+            testListener.pushExpectedEvent(NextEvent.CHANGE);
+            
+            Interval it = new Interval(clock.getUTCNow(), clock.getUTCNow().plusMonths(1));
+            clock.addDeltaFromReality(it.toDurationMillis());
+            assertTrue(testListener.isCompleted(5000));
 
             assertDateWithin(subscription.getStartDate(), beforeMigration, afterMigration);
             assertEquals(subscription.getEndDate(), null);
@@ -255,6 +272,8 @@ public abstract class TestMigration extends TestApiBase {
             assertEquals(subscription.getState(), SubscriptionState.ACTIVE);
             assertEquals(subscription.getCurrentPlan().getName(), "shotgun-annual");
 
+            assertListenerStatus();
+            
         } catch (EntitlementMigrationApiException e) {
             Assert.fail("", e);
         }
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/api/migration/TestMigrationMemory.java b/entitlement/src/test/java/com/ning/billing/entitlement/api/migration/TestMigrationMemory.java
index 6ea53bc..0195d69 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/api/migration/TestMigrationMemory.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/api/migration/TestMigrationMemory.java
@@ -31,26 +31,26 @@ public class TestMigrationMemory extends TestMigration {
     }
 
     @Override
-    @Test(enabled=false, groups="fast")
+    @Test(enabled=true, groups="fast")
     public void testSingleBasePlan() {
         super.testSingleBasePlan();
     }
 
     @Override
-    @Test(enabled=false, groups="fast")
+    @Test(enabled=true, groups="fast")
     public void testSingleBasePlanFutureCancelled() {
         super.testSingleBasePlanFutureCancelled();
     }
 
     @Override
-    @Test(enabled=false, groups="fast")
+    @Test(enabled=true, groups="fast")
     public void testPlanWithAddOn() {
         super.testPlanWithAddOn();
     }
 
 
     @Override
-    @Test(enabled=false, groups="fast")
+    @Test(enabled=true, groups="fast")
     public void testSingleBasePlanWithPendingPhase() {
         super.testSingleBasePlanWithPendingPhase();
     }
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 8289ca9..7d1389e 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
@@ -34,6 +34,7 @@ import org.joda.time.DateTimeZone;
 import org.joda.time.Period;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
+import org.testng.Assert;
 import org.testng.annotations.AfterClass;
 import org.testng.annotations.AfterMethod;
 import org.testng.annotations.BeforeClass;
@@ -42,6 +43,9 @@ import org.testng.annotations.BeforeMethod;
 import com.google.inject.Injector;
 import com.google.inject.Key;
 import com.ning.billing.account.api.AccountData;
+import com.ning.billing.api.TestApiListener;
+import com.ning.billing.api.TestApiListener.NextEvent;
+import com.ning.billing.api.TestListenerStatus;
 import com.ning.billing.catalog.DefaultCatalogService;
 import com.ning.billing.catalog.api.BillingPeriod;
 import com.ning.billing.catalog.api.Catalog;
@@ -54,7 +58,6 @@ import com.ning.billing.catalog.api.ProductCategory;
 import com.ning.billing.catalog.api.TimeUnit;
 import com.ning.billing.config.EntitlementConfig;
 import com.ning.billing.dbi.MysqlTestingHelper;
-import com.ning.billing.entitlement.api.ApiTestListener.NextEvent;
 import com.ning.billing.entitlement.api.billing.ChargeThruApi;
 import com.ning.billing.entitlement.api.migration.EntitlementMigrationApi;
 import com.ning.billing.entitlement.api.timeline.EntitlementTimelineApi;
@@ -80,7 +83,8 @@ import com.ning.billing.util.clock.ClockMock;
 import com.ning.billing.util.glue.RealImplementation;
 
 
-public abstract class TestApiBase {
+public abstract class TestApiBase implements TestListenerStatus {
+    
     protected static final Logger log = LoggerFactory.getLogger(TestApiBase.class);
 
     protected static final long DAY_IN_MS = (24 * 3600 * 1000);
@@ -100,12 +104,23 @@ public abstract class TestApiBase {
 
     protected AccountData accountData;
     protected Catalog catalog;
-    protected ApiTestListener testListener;
+    protected TestApiListener testListener;
     protected SubscriptionBundle bundle;
 
     private MysqlTestingHelper helper;
     protected CallContext context = new TestCallContext("Api Test");
 
+    private boolean isListenerFailed;
+    private String listenerFailedMsg;    
+
+    //
+    // The date on which we make our test start; just to ensure that running tests at different dates does not
+    // produce different results. nothing specific about that date; we could change it to anything.
+    //
+    protected DateTime testStartDate = new DateTime(2012, 5, 7, 0, 3, 42, 0);
+
+
+    
     public static void loadSystemPropertiesFromClasspath(final String resource) {
         final URL url = TestApiBase.class.getResource(resource);
         assertNotNull(url);
@@ -131,6 +146,19 @@ public abstract class TestApiBase {
         }
     }
 
+    
+    @Override
+    public void failed(final String msg) {
+        this.isListenerFailed = true;
+        this.listenerFailedMsg = msg;
+    }
+
+    @Override
+    public void resetTestListenerStatus() {
+        this.isListenerFailed = false;
+        this.listenerFailedMsg = null;
+    }
+    
     @BeforeClass(alwaysRun = true)
     public void setup() throws Exception {
 
@@ -149,18 +177,24 @@ public abstract class TestApiBase {
         dao = g.getInstance(EntitlementDao.class);
         clock = (ClockMock) g.getInstance(Clock.class);
         helper = (isSqlTest(dao)) ? g.getInstance(MysqlTestingHelper.class) : null;
+        init();
+    }
+
+    private void init() throws Exception {
+
+        setupDao();
 
         ((DefaultCatalogService) catalogService).loadCatalog();
-        ((DefaultBusService) busService).startBus();
         ((Engine) entitlementService).initialize();
-        init(g);
-    }
 
-    private static boolean isSqlTest(EntitlementDao theDao) {
-        return (! (theDao instanceof MockEntitlementDaoMemory));
+        accountData = getAccountData();
+        assertNotNull(accountData);
+        catalog = catalogService.getFullCatalog();
+        assertNotNull(catalog);
+        testListener = new TestApiListener(this);
     }
 
-    private void setupMySQL() throws IOException {
+    private void setupDao() throws IOException {
         if (helper != null) {
             final String entitlementDdl = IOUtils.toString(TestApiBase.class.getResourceAsStream("/com/ning/billing/entitlement/ddl.sql"));
             final String utilDdl = IOUtils.toString(TestApiBase.class.getResourceAsStream("/com/ning/billing/util/ddl.sql"));
@@ -170,47 +204,70 @@ public abstract class TestApiBase {
         }
     }
 
-    private void init(Injector g) throws Exception {
-
-        setupMySQL();
-        accountData = getAccountData();
-        assertNotNull(accountData);
-        catalog = catalogService.getFullCatalog();
-        assertNotNull(catalog);
-        testListener = new ApiTestListener(busService.getBus());
+    private static boolean isSqlTest(EntitlementDao theDao) {
+        return (! (theDao instanceof MockEntitlementDaoMemory));
     }
-
+   
     @BeforeMethod(alwaysRun = true)
     public void setupTest() throws Exception {
 
         log.warn("RESET TEST FRAMEWORK\n\n");
 
+        // CLEANUP ALL DB TABLES OR IN MEMORY STRUCTURES
+        cleanupDao();
+        
+        // RESET LIST OF EXPECTED EVENTS
         if (testListener != null) {
             testListener.reset();
+            resetTestListenerStatus();
         }
-
+        
+        // RESET CLOCK
         clock.resetDeltaFromReality();
-        ((MockEntitlementDao) dao).reset();
 
+        // START BUS AND REGISTER LISTENER
+        busService.getBus().start();
         busService.getBus().register(testListener);
+        
+        // START NOTIFICATION QUEUE FOR ENTITLEMENT
+        ((Engine)entitlementService).start();
+        
+        // SETUP START DATE
+        clock.setDeltaFromReality(testStartDate.getMillis() - clock.getUTCNow().getMillis());
+        
+        // CREATE NEW BUNDLE FOR TEST
         UUID accountId = UUID.randomUUID();
         bundle = entitlementApi.createBundleForAccount(accountId, "myDefaultBundle", context);
         assertNotNull(bundle);
-
-        ((Engine)entitlementService).start();
     }
 
     @AfterMethod(alwaysRun = true)
     public void cleanupTest() throws Exception {
-        if (busService != null) {
-            busService.getBus().unregister(testListener);
-        }
+        
+        // UNREGISTER TEST LISTENER AND STOP BUS
+        busService.getBus().unregister(testListener);
+        busService.getBus().stop();
+        
+        // STOP NOTIFICATION QUEUE
+        ((Engine)entitlementService).stop();
 
-        if (entitlementService != null) {
-            ((Engine)entitlementService).stop();
-        }
         log.warn("DONE WITH TEST\n");
     }
+    
+    protected void assertListenerStatus() {
+        if (isListenerFailed) {
+            log.error(listenerFailedMsg);
+            Assert.fail(listenerFailedMsg);
+        }
+    }
+    
+    private void cleanupDao() {
+        if (helper != null) {
+            helper.cleanupAllTables();
+        } else {
+            ((MockEntitlementDao) dao).reset();
+        }
+    }
 
     protected SubscriptionData createSubscription(final String productName, final BillingPeriod term, final String planSet, final DateTime requestedDate)
         throws EntitlementUserApiException {
@@ -223,13 +280,13 @@ public abstract class TestApiBase {
 
     protected SubscriptionData createSubscriptionWithBundle(final UUID bundleId, final String productName, final BillingPeriod term, final String planSet, final DateTime requestedDate)
         throws EntitlementUserApiException {
-        testListener.pushNextApiExpectedEvent(NextEvent.CREATE);
-
+        
+        testListener.pushExpectedEvent(NextEvent.CREATE);
         SubscriptionData subscription = (SubscriptionData) entitlementApi.createSubscription(bundleId,
                 new PlanPhaseSpecifier(productName, ProductCategory.BASE, term, planSet, null),
                 requestedDate == null ? clock.getUTCNow() : requestedDate, context);
         assertNotNull(subscription);
-        assertTrue(testListener.isApiCompleted(5000));
+        assertTrue(testListener.isCompleted(5000));
         return subscription;
     }
 
@@ -325,7 +382,7 @@ public abstract class TestApiBase {
 
             @Override
             public DateTime addToDateTime(DateTime dateTime) {
-                return null;  //To change body of implemented methods use File | Settings | File Templates.
+                return dateTime.plusYears(years);  
             }
             @Override
             public Period toJodaPeriod() {
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 e988805..9e864cd 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
@@ -21,21 +21,20 @@ import static org.testng.Assert.assertNull;
 import static org.testng.Assert.assertTrue;
 
 import java.util.Collections;
-import java.util.Comparator;
 import java.util.LinkedList;
 import java.util.List;
 import java.util.UUID;
 
 import org.joda.time.DateTime;
-import org.testng.Assert;
+import org.joda.time.Interval;
 import org.testng.annotations.Test;
 
 import com.google.inject.Guice;
 import com.google.inject.Injector;
 import com.google.inject.Stage;
 import com.ning.billing.ErrorCode;
+import com.ning.billing.api.TestApiListener.NextEvent;
 import com.ning.billing.catalog.api.BillingPeriod;
-import com.ning.billing.catalog.api.Duration;
 import com.ning.billing.catalog.api.PhaseType;
 import com.ning.billing.catalog.api.Plan;
 import com.ning.billing.catalog.api.PlanPhase;
@@ -43,10 +42,6 @@ import com.ning.billing.catalog.api.PlanPhaseSpecifier;
 import com.ning.billing.catalog.api.PriceListSet;
 import com.ning.billing.catalog.api.ProductCategory;
 import com.ning.billing.entitlement.api.SubscriptionTransitionType;
-import com.ning.billing.entitlement.api.ApiTestListener.NextEvent;
-import com.ning.billing.entitlement.api.timeline.BundleTimeline;
-import com.ning.billing.entitlement.api.timeline.EntitlementRepairException;
-import com.ning.billing.entitlement.api.timeline.SubscriptionTimeline;
 import com.ning.billing.entitlement.api.timeline.SubscriptionTimeline.DeletedEvent;
 import com.ning.billing.entitlement.api.timeline.SubscriptionTimeline.ExistingEvent;
 import com.ning.billing.entitlement.api.timeline.SubscriptionTimeline.NewEvent;
@@ -67,6 +62,8 @@ public class TestRepairBP extends TestApiBaseRepair {
     @Test(groups={"slow"})
     public void testFetchBundleRepair() throws Exception  {
 
+        log.info("Starting testFetchBundleRepair");
+        
         String baseProduct = "Shotgun";
         BillingPeriod baseTerm = BillingPeriod.MONTHLY;
         String basePriceList = PriceListSet.DEFAULT_PRICELIST_NAME;
@@ -125,11 +122,14 @@ public class TestRepairBP extends TestApiBaseRepair {
                 assertEquals(events.get(1).getPlanPhaseSpecifier().getBillingPeriod(), aoTerm);                    
             }
         }
+        assertListenerStatus();
     }
     
     @Test(groups={"slow"})
     public void testBPRepairWithCancellationOnstart() throws Exception {
 
+        log.info("Starting testBPRepairWithCancellationOnstart");
+        
         String baseProduct = "Shotgun";
         DateTime startDate = clock.getUTCNow();
         
@@ -137,8 +137,8 @@ public class TestRepairBP extends TestApiBaseRepair {
         Subscription baseSubscription = createSubscription(baseProduct, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, startDate);
 
         // Stays in trial-- for instance
-        Duration durationShift = getDurationDay(10);
-        clock.setDeltaFromReality(durationShift, 0);
+        Interval it = new Interval(clock.getUTCNow(), clock.getUTCNow().plusDays(10));
+        clock.addDeltaFromReality(it.toDurationMillis());
 
         BundleTimeline bundleRepair = repairApi.getBundleRepair(bundle.getId());
         sortEventsOnBundle(bundleRepair);
@@ -190,9 +190,9 @@ public class TestRepairBP extends TestApiBaseRepair {
         
        // SECOND RE-ISSUE CALL-- NON DRY RUN
         dryRun = false;
-        testListener.expectRepairCompletion();
+        testListener.pushExpectedEvent(NextEvent.REPAIR_BUNDLE);
         BundleTimeline realRunBundleRepair = repairApi.repairBundle(bRepair, dryRun, context);
-        assertTrue(testListener.isRepairCompleted(5000));
+        assertTrue(testListener.isCompleted(5000));
         
         subscriptionRepair = realRunBundleRepair.getSubscriptions();
         assertEquals(subscriptionRepair.size(), 1);
@@ -211,10 +211,15 @@ public class TestRepairBP extends TestApiBaseRepair {
         assertEquals(realRunBaseSubscription.getStartDate(), startDate);
 
         assertEquals(realRunBaseSubscription.getState(), SubscriptionState.CANCELLED);
+        
+        assertListenerStatus();
     }
     
     @Test(groups={"slow"})
     public void testBPRepairReplaceCreateBeforeTrial() throws Exception {
+        
+        log.info("Starting testBPRepairReplaceCreateBeforeTrial");
+        
         String baseProduct = "Shotgun";
         String newBaseProduct = "Assault-Rifle";
         
@@ -229,10 +234,14 @@ public class TestRepairBP extends TestApiBaseRepair {
                     ProductCategory.BASE, PriceListSet.DEFAULT_PRICELIST_NAME, BillingPeriod.MONTHLY, restartDate.plusDays(30)));
 
         testBPRepairCreate(true, startDate, clockShift, baseProduct, newBaseProduct, expected);
+        assertListenerStatus();
     }
 
     @Test(groups={"slow"}, enabled=true)
     public void testBPRepairReplaceCreateInTrial() throws Exception {
+        
+        log.info("Starting testBPRepairReplaceCreateInTrial");
+        
         String baseProduct = "Shotgun";
         String newBaseProduct = "Assault-Rifle";
         
@@ -248,9 +257,10 @@ public class TestRepairBP extends TestApiBaseRepair {
 
         UUID baseSubscriptionId = testBPRepairCreate(true, startDate, clockShift, baseProduct, newBaseProduct, expected);
         
-        testListener.pushNextApiExpectedEvent(NextEvent.PHASE);
-        clock.addDeltaFromReality(getDurationDay(32));
-        assertTrue(testListener.isApiCompleted(5000));
+        testListener.pushExpectedEvent(NextEvent.PHASE);
+        Interval it = new Interval(clock.getUTCNow(), clock.getUTCNow().plusDays(32));
+        clock.addDeltaFromReality(it.toDurationMillis());
+        assertTrue(testListener.isCompleted(5000));
         
         // CHECK WHAT"S GOING ON AFTER WE MOVE CLOCK-- FUTURE MOTIFICATION SHOULD KICK IN
         SubscriptionData subscription = (SubscriptionData) entitlementApi.getSubscriptionFromId(baseSubscriptionId);
@@ -269,11 +279,16 @@ public class TestRepairBP extends TestApiBaseRepair {
         PlanPhase currentPhase = subscription.getCurrentPhase();
         assertNotNull(currentPhase);
         assertEquals(currentPhase.getPhaseType(), PhaseType.EVERGREEN);
+        
+        assertListenerStatus();
     }
 
     
     @Test(groups={"slow"})
     public void testBPRepairReplaceCreateAfterTrial() throws Exception {
+        
+        log.info("Starting testBPRepairReplaceCreateAfterTrial");
+        
         String baseProduct = "Shotgun";
         String newBaseProduct = "Assault-Rifle";
         
@@ -288,25 +303,28 @@ public class TestRepairBP extends TestApiBaseRepair {
                     ProductCategory.BASE, PriceListSet.DEFAULT_PRICELIST_NAME, BillingPeriod.MONTHLY, restartDate.plusDays(30)));
 
         testBPRepairCreate(false, startDate, clockShift, baseProduct, newBaseProduct, expected);
-        
+        assertListenerStatus();
     }
     
     
     private UUID testBPRepairCreate(boolean inTrial, DateTime startDate, int clockShift, 
             String baseProduct, String newBaseProduct, List<ExistingEvent> expectedEvents) throws Exception {
 
+        log.info("Starting testBPRepairCreate");
+        
         // CREATE BP
         Subscription baseSubscription = createSubscription(baseProduct, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, startDate);
 
         // MOVE CLOCK
         if (clockShift > 0) {
             if (!inTrial) {
-                testListener.pushNextApiExpectedEvent(NextEvent.PHASE);
+                testListener.pushExpectedEvent(NextEvent.PHASE);
             }               
-            Duration durationShift = getDurationDay(clockShift);
-            clock.setDeltaFromReality(durationShift, 0);
+            
+            Interval it = new Interval(clock.getUTCNow(), clock.getUTCNow().plusDays(clockShift));
+            clock.addDeltaFromReality(it.toDurationMillis());
             if (!inTrial) {
-                assertTrue(testListener.isApiCompleted(5000));
+                assertTrue(testListener.isCompleted(5000));
             }
         }
 
@@ -327,7 +345,7 @@ public class TestRepairBP extends TestApiBaseRepair {
         // FIRST ISSUE DRY RUN
         BundleTimeline bRepair =  createBundleRepair(bundle.getId(), bundleRepair.getViewId(), Collections.singletonList(sRepair));
         
-        boolean dryRun = true;
+        boolean dryRun = true;        
         BundleTimeline dryRunBundleRepair = repairApi.repairBundle(bRepair, dryRun, context);
         List<SubscriptionTimeline> subscriptionRepair = dryRunBundleRepair.getSubscriptions();
         assertEquals(subscriptionRepair.size(), 1);
@@ -362,8 +380,9 @@ public class TestRepairBP extends TestApiBaseRepair {
         
        // SECOND RE-ISSUE CALL-- NON DRY RUN
         dryRun = false;
+        testListener.pushExpectedEvent(NextEvent.REPAIR_BUNDLE);
         BundleTimeline realRunBundleRepair = repairApi.repairBundle(bRepair, dryRun, context);
-
+        assertTrue(testListener.isCompleted(5000));
         subscriptionRepair = realRunBundleRepair.getSubscriptions();
         assertEquals(subscriptionRepair.size(), 1);
         cur = subscriptionRepair.get(0);
@@ -402,6 +421,8 @@ public class TestRepairBP extends TestApiBaseRepair {
     @Test(groups={"slow"})
     public void testBPRepairAddChangeInTrial() throws Exception {
         
+        log.info("Starting testBPRepairAddChangeInTrial");
+        
         String baseProduct = "Shotgun";
         String newBaseProduct = "Assault-Rifle";
         
@@ -420,9 +441,10 @@ public class TestRepairBP extends TestApiBaseRepair {
         UUID baseSubscriptionId = testBPRepairAddChange(true, startDate, clockShift, baseProduct, newBaseProduct, expected, 3);
         
         // CHECK WHAT"S GOING ON AFTER WE MOVE CLOCK-- FUTURE MOTIFICATION SHOULD KICK IN
-        testListener.pushNextApiExpectedEvent(NextEvent.PHASE);
-        clock.addDeltaFromReality(getDurationDay(32));
-        assertTrue(testListener.isApiCompleted(5000));
+        testListener.pushExpectedEvent(NextEvent.PHASE);
+        Interval it = new Interval(clock.getUTCNow(), clock.getUTCNow().plusDays(32));
+        clock.addDeltaFromReality(it.toDurationMillis());
+        assertTrue(testListener.isCompleted(5000));
         SubscriptionData subscription = (SubscriptionData) entitlementApi.getSubscriptionFromId(baseSubscriptionId);
         
         assertEquals(subscription.getActiveVersion(), SubscriptionEvents.INITIAL_VERSION + 1);
@@ -439,11 +461,15 @@ public class TestRepairBP extends TestApiBaseRepair {
         PlanPhase currentPhase = subscription.getCurrentPhase();
         assertNotNull(currentPhase);
         assertEquals(currentPhase.getPhaseType(), PhaseType.EVERGREEN);
+        
+        assertListenerStatus();
     }
 
     @Test(groups={"slow"})
     public void testBPRepairAddChangeAfterTrial() throws Exception {
         
+        log.info("Starting testBPRepairAddChangeAfterTrial");
+        
         String baseProduct = "Shotgun";
         String newBaseProduct = "Assault-Rifle";
         
@@ -459,24 +485,27 @@ public class TestRepairBP extends TestApiBaseRepair {
         expected.add(createExistingEventForAssertion(SubscriptionTransitionType.CHANGE, newBaseProduct, PhaseType.EVERGREEN,
                 ProductCategory.BASE, PriceListSet.DEFAULT_PRICELIST_NAME, BillingPeriod.MONTHLY, changeDate));
         testBPRepairAddChange(false, startDate, clockShift, baseProduct, newBaseProduct, expected, 3);
+    
+        assertListenerStatus();
     }
     
 
     private UUID testBPRepairAddChange(boolean inTrial, DateTime startDate, int clockShift, 
             String baseProduct, String newBaseProduct, List<ExistingEvent> expectedEvents, int expectedTransitions) throws Exception {
 
+        
         // CREATE BP
         Subscription baseSubscription = createSubscription(baseProduct, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, startDate);
 
         // MOVE CLOCK
         if (!inTrial) {
-            testListener.pushNextApiExpectedEvent(NextEvent.PHASE);
+            testListener.pushExpectedEvent(NextEvent.PHASE);
         }               
-
-        Duration durationShift = getDurationDay(clockShift);
-        clock.setDeltaFromReality(durationShift, 0);
+        
+        Interval it = new Interval(clock.getUTCNow(), clock.getUTCNow().plusDays(clockShift));
+        clock.addDeltaFromReality(it.toDurationMillis());
         if (!inTrial) {
-            assertTrue(testListener.isApiCompleted(5000));
+            assertTrue(testListener.isCompleted(5000));
         }
 
         BundleTimeline bundleRepair = repairApi.getBundleRepair(bundle.getId());
@@ -498,6 +527,7 @@ public class TestRepairBP extends TestApiBaseRepair {
         
         boolean dryRun = true;
         BundleTimeline dryRunBundleRepair = repairApi.repairBundle(bRepair, dryRun, context);
+        
         List<SubscriptionTimeline> subscriptionRepair = dryRunBundleRepair.getSubscriptions();
         assertEquals(subscriptionRepair.size(), 1);
         SubscriptionTimeline cur = subscriptionRepair.get(0);
@@ -532,7 +562,9 @@ public class TestRepairBP extends TestApiBaseRepair {
         
        // SECOND RE-ISSUE CALL-- NON DRY RUN
         dryRun = false;
+        testListener.pushExpectedEvent(NextEvent.REPAIR_BUNDLE);
         BundleTimeline realRunBundleRepair = repairApi.repairBundle(bRepair, dryRun, context);
+        assertTrue(testListener.isCompleted(5000));
 
         subscriptionRepair = realRunBundleRepair.getSubscriptions();
         assertEquals(subscriptionRepair.size(), 1);
@@ -572,15 +604,19 @@ public class TestRepairBP extends TestApiBaseRepair {
     @Test(groups={"slow"})
     public void testRepairWithFurureCancelEvent() throws Exception {
       
+        log.info("Starting testRepairWithFurureCancelEvent");
+        
         DateTime startDate = clock.getUTCNow();
         
         // CREATE BP
         Subscription baseSubscription = createSubscription("Shotgun", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, startDate);
 
         // MOVE CLOCK -- OUT OF TRIAL
-        testListener.pushNextApiExpectedEvent(NextEvent.PHASE);                
-        clock.setDeltaFromReality(getDurationDay(35), 0);
-        assertTrue(testListener.isApiCompleted(5000));
+        testListener.pushExpectedEvent(NextEvent.PHASE);
+        
+        Interval it = new Interval(clock.getUTCNow(), clock.getUTCNow().plusDays(35));
+        clock.addDeltaFromReality(it.toDurationMillis());
+        assertTrue(testListener.isCompleted(5000));
         
         // SET CTD to BASE SUBSCRIPTION SP CANCEL OCCURS EOT
         DateTime newChargedThroughDate = baseSubscription.getStartDate().plusDays(30).plusMonths(1);
@@ -616,7 +652,9 @@ public class TestRepairBP extends TestApiBaseRepair {
         BundleTimeline bRepair =  createBundleRepair(bundle.getId(), bundleRepair.getViewId(), Collections.singletonList(sRepair));
         
         boolean dryRun = false;
+        testListener.pushExpectedEvent(NextEvent.REPAIR_BUNDLE);
         repairApi.repairBundle(bRepair, dryRun, context);
+        assertTrue(testListener.isCompleted(5000));
      
         baseSubscription = (SubscriptionData) entitlementApi.getSubscriptionFromId(baseSubscription.getId());
         
@@ -633,6 +671,8 @@ public class TestRepairBP extends TestApiBaseRepair {
         PlanPhase currentPhase = baseSubscription.getCurrentPhase();
         assertNotNull(currentPhase);
         assertEquals(currentPhase.getPhaseType(), PhaseType.EVERGREEN);
+        
+        assertListenerStatus();
     }
     
     
@@ -640,12 +680,11 @@ public class TestRepairBP extends TestApiBaseRepair {
     @Test(groups={"slow"})
     public void testENT_REPAIR_VIEW_CHANGED_newEvent() throws Exception {
        
+        log.info("Starting testENT_REPAIR_VIEW_CHANGED_newEvent");
+        
         TestWithException test = new TestWithException();
         DateTime startDate = clock.getUTCNow();
         
-        testListener.reset();
-        clock.resetDeltaFromReality();
-
         final Subscription baseSubscription = createSubscription("Shotgun", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, startDate);
         
         test.withException(new TestWithExceptionCallback() {
@@ -663,12 +702,13 @@ public class TestRepairBP extends TestApiBaseRepair {
 
                 BundleTimeline bRepair =  createBundleRepair(bundle.getId(), bundleRepair.getViewId(), Collections.singletonList(sRepair));
 
-                testListener.pushNextApiExpectedEvent(NextEvent.CHANGE);
+                testListener.pushExpectedEvent(NextEvent.CHANGE);
                 DateTime changeTime = clock.getUTCNow();
                 baseSubscription.changePlan("Assault-Rifle", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, changeTime, context);
-                assertTrue(testListener.isApiCompleted(5000));
-                                
+                assertTrue(testListener.isCompleted(5000));
+                
                 repairApi.repairBundle(bRepair, true, context);
+                assertListenerStatus();
             }
         }, ErrorCode.ENT_REPAIR_VIEW_CHANGED);
     }
@@ -676,12 +716,11 @@ public class TestRepairBP extends TestApiBaseRepair {
     @Test(groups={"slow"}, enabled=false)
     public void testENT_REPAIR_VIEW_CHANGED_ctd() throws Exception {
        
+        log.info("Starting testENT_REPAIR_VIEW_CHANGED_ctd");
+        
         TestWithException test = new TestWithException();
         DateTime startDate = clock.getUTCNow();
         
-        testListener.reset();
-        clock.resetDeltaFromReality();
-
         final Subscription baseSubscription = createSubscription("Shotgun", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, startDate);
         
         test.withException(new TestWithExceptionCallback() {
@@ -704,6 +743,8 @@ public class TestRepairBP extends TestApiBaseRepair {
                 entitlementApi.getSubscriptionFromId(baseSubscription.getId());
 
                 repairApi.repairBundle(bRepair, true, context);
+                
+                assertListenerStatus();
             }
         }, ErrorCode.ENT_REPAIR_VIEW_CHANGED);
     }
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 57a78e6..d7fd642 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
@@ -24,13 +24,14 @@ import static org.testng.Assert.assertNotNull;
 import static org.testng.Assert.assertTrue;
 
 import org.joda.time.DateTime;
+import org.joda.time.Interval;
 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.Duration;
 import com.ning.billing.catalog.api.PhaseType;
 import com.ning.billing.catalog.api.Plan;
 import com.ning.billing.catalog.api.PlanPhase;
@@ -38,9 +39,6 @@ import com.ning.billing.catalog.api.PlanPhaseSpecifier;
 import com.ning.billing.catalog.api.PriceListSet;
 import com.ning.billing.catalog.api.ProductCategory;
 import com.ning.billing.entitlement.api.SubscriptionTransitionType;
-import com.ning.billing.entitlement.api.ApiTestListener.NextEvent;
-import com.ning.billing.entitlement.api.timeline.BundleTimeline;
-import com.ning.billing.entitlement.api.timeline.SubscriptionTimeline;
 import com.ning.billing.entitlement.api.timeline.SubscriptionTimeline.DeletedEvent;
 import com.ning.billing.entitlement.api.timeline.SubscriptionTimeline.ExistingEvent;
 import com.ning.billing.entitlement.api.timeline.SubscriptionTimeline.NewEvent;
@@ -59,6 +57,8 @@ public class TestRepairWithAO extends TestApiBaseRepair {
     @Test(groups={"slow"})
     public void testRepairChangeBPWithAddonIncluded() throws Exception {
         
+        log.info("Starting testRepairChangeBPWithAddonIncluded");
+        
         String baseProduct = "Shotgun";
         BillingPeriod baseTerm = BillingPeriod.MONTHLY;
         String basePriceList = PriceListSet.DEFAULT_PRICELIST_NAME;
@@ -67,15 +67,16 @@ public class TestRepairWithAO extends TestApiBaseRepair {
         SubscriptionData baseSubscription = createSubscription(baseProduct, baseTerm, basePriceList);
 
         // MOVE CLOCK A LITTLE BIT-- STILL IN TRIAL
-        Duration someTimeLater = getDurationDay(3);
-        clock.setDeltaFromReality(someTimeLater, DAY_IN_MS);
+        Interval it = new Interval(clock.getUTCNow(), clock.getUTCNow().plusDays(4));
+        clock.addDeltaFromReality(it.toDurationMillis());
 
         SubscriptionData aoSubscription = createSubscription("Telescopic-Scope", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME);
         
         SubscriptionData aoSubscription2 = createSubscription("Laser-Scope", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME);
 
         // MOVE CLOCK A LITTLE BIT MORE -- STILL IN TRIAL
-        clock.addDeltaFromReality(someTimeLater);
+        it = new Interval(clock.getUTCNow(), clock.getUTCNow().plusDays(3));
+        clock.addDeltaFromReality(it.toDurationMillis());
 
         BundleTimeline bundleRepair = repairApi.getBundleRepair(bundle.getId());
         sortEventsOnBundle(bundleRepair);
@@ -165,8 +166,11 @@ public class TestRepairWithAO extends TestApiBaseRepair {
         assertEquals(newBaseSubscription.getAllTransitions().size(), 2);
         assertEquals(newBaseSubscription.getActiveVersion(), SubscriptionEvents.INITIAL_VERSION);
         
-        dryRun = false;
+        dryRun = false;        
+        testListener.pushExpectedEvent(NextEvent.REPAIR_BUNDLE);
         BundleTimeline realRunBundleRepair = repairApi.repairBundle(bundleRepair, dryRun, context);
+        assertTrue(testListener.isCompleted(5000));
+
         
         
         aoRepair = getSubscriptionRepair(aoSubscription.getId(), realRunBundleRepair);
@@ -210,6 +214,8 @@ public class TestRepairWithAO extends TestApiBaseRepair {
     @Test(groups={"slow"})
     public void testRepairChangeBPWithAddonNonAvailable() throws Exception {
         
+        log.info("Starting testRepairChangeBPWithAddonNonAvailable");
+        
         String baseProduct = "Shotgun";
         BillingPeriod baseTerm = BillingPeriod.MONTHLY;
         String basePriceList = PriceListSet.DEFAULT_PRICELIST_NAME;
@@ -218,17 +224,18 @@ public class TestRepairWithAO extends TestApiBaseRepair {
         SubscriptionData baseSubscription = createSubscription(baseProduct, baseTerm, basePriceList);
 
         // MOVE CLOCK A LITTLE BIT-- STILL IN TRIAL
-        Duration someTimeLater = getDurationDay(3);
-        clock.setDeltaFromReality(someTimeLater, DAY_IN_MS);
+        Interval it = new Interval(clock.getUTCNow(), clock.getUTCNow().plusDays(3));
+        clock.addDeltaFromReality(it.toDurationMillis());
 
         SubscriptionData aoSubscription = createSubscription("Telescopic-Scope", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME);
         
         // MOVE CLOCK A LITTLE BIT MORE -- AFTER TRIAL
-        testListener.pushNextApiExpectedEvent(NextEvent.PHASE);
-        testListener.pushNextApiExpectedEvent(NextEvent.PHASE);
-        someTimeLater = getDurationDay(32);
-        clock.addDeltaFromReality(someTimeLater);
-        assertTrue(testListener.isApiCompleted(7000));        
+        testListener.pushExpectedEvent(NextEvent.PHASE);
+        testListener.pushExpectedEvent(NextEvent.PHASE);
+        
+        it = new Interval(clock.getUTCNow(), clock.getUTCNow().plusDays(32));
+        clock.addDeltaFromReality(it.toDurationMillis());
+        assertTrue(testListener.isCompleted(7000));        
 
         BundleTimeline bundleRepair = repairApi.getBundleRepair(bundle.getId());
         sortEventsOnBundle(bundleRepair);
@@ -295,8 +302,10 @@ public class TestRepairWithAO extends TestApiBaseRepair {
         assertEquals(newBaseSubscription.getActiveVersion(), SubscriptionEvents.INITIAL_VERSION);
         
         dryRun = false;
+        testListener.pushExpectedEvent(NextEvent.REPAIR_BUNDLE);
         BundleTimeline realRunBundleRepair = repairApi.repairBundle(bundleRepair, dryRun, context);
-        
+        assertTrue(testListener.isCompleted(5000));
+
         aoRepair = getSubscriptionRepair(aoSubscription.getId(), realRunBundleRepair);
         assertEquals(aoRepair.getExistingEvents().size(), 3);
 
@@ -327,6 +336,8 @@ public class TestRepairWithAO extends TestApiBaseRepair {
     @Test(groups={"slow"})
     public void testRepairCancelBP_EOT_WithAddons() throws Exception {
         
+        log.info("Starting testRepairCancelBP_EOT_WithAddons");
+        
         String baseProduct = "Shotgun";
         BillingPeriod baseTerm = BillingPeriod.MONTHLY;
         String basePriceList = PriceListSet.DEFAULT_PRICELIST_NAME;
@@ -335,17 +346,19 @@ public class TestRepairWithAO extends TestApiBaseRepair {
         SubscriptionData baseSubscription = createSubscription(baseProduct, baseTerm, basePriceList);
 
         // MOVE CLOCK A LITTLE BIT-- STILL IN TRIAL
-        Duration someTimeLater = getDurationDay(3);
-        clock.setDeltaFromReality(someTimeLater, DAY_IN_MS);
+        Interval it = new Interval(clock.getUTCNow(), clock.getUTCNow().plusDays(4));
+        clock.addDeltaFromReality(it.toDurationMillis());
+
 
         SubscriptionData aoSubscription = createSubscription("Telescopic-Scope", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME);
         
         // MOVE CLOCK A LITTLE BIT MORE -- AFTER TRIAL
-        testListener.pushNextApiExpectedEvent(NextEvent.PHASE);
-        testListener.pushNextApiExpectedEvent(NextEvent.PHASE);
-        someTimeLater = getDurationDay(40);
-        clock.addDeltaFromReality(someTimeLater);
-        assertTrue(testListener.isApiCompleted(7000));
+        testListener.pushExpectedEvent(NextEvent.PHASE);
+        testListener.pushExpectedEvent(NextEvent.PHASE);
+        
+        it = new Interval(clock.getUTCNow(), clock.getUTCNow().plusDays(40));
+        clock.addDeltaFromReality(it.toDurationMillis());
+        assertTrue(testListener.isCompleted(7000));
         
         // SET CTD to BASE SUBSCRIPTION SP CANCEL OCCURS EOT
         DateTime newChargedThroughDate = baseSubscription.getStartDate().plusDays(30).plusMonths(1);
@@ -414,7 +427,9 @@ public class TestRepairWithAO extends TestApiBaseRepair {
         assertEquals(newBaseSubscription.getActiveVersion(), SubscriptionEvents.INITIAL_VERSION);
         
         dryRun = false;
+        testListener.pushExpectedEvent(NextEvent.REPAIR_BUNDLE);
         BundleTimeline realRunBundleRepair = repairApi.repairBundle(bundleRepair, dryRun, context);
+        assertTrue(testListener.isCompleted(5000));
         
         aoRepair = getSubscriptionRepair(aoSubscription.getId(), realRunBundleRepair);
         assertEquals(aoRepair.getExistingEvents().size(), 3);
@@ -443,11 +458,12 @@ public class TestRepairWithAO extends TestApiBaseRepair {
         assertEquals(newBaseSubscription.getActiveVersion(), SubscriptionEvents.INITIAL_VERSION + 1);
         
         // MOVE CLOCK AFTER CANCEL DATE
-        testListener.pushNextApiExpectedEvent(NextEvent.CANCEL);
-        testListener.pushNextApiExpectedEvent(NextEvent.CANCEL);
-        someTimeLater = getDurationDay(32);
-        clock.addDeltaFromReality(someTimeLater);
-        assertTrue(testListener.isApiCompleted(7000));
+        testListener.pushExpectedEvent(NextEvent.CANCEL);
+        testListener.pushExpectedEvent(NextEvent.CANCEL);
+        
+        it = new Interval(clock.getUTCNow(), clock.getUTCNow().plusDays(32));
+        clock.addDeltaFromReality(it.toDurationMillis());
+        assertTrue(testListener.isCompleted(7000));
 
         newAoSubscription = (SubscriptionData)  entitlementApi.getSubscriptionFromId(aoSubscription.getId());
         assertEquals(newAoSubscription.getState(), SubscriptionState.CANCELLED);
@@ -464,6 +480,9 @@ public class TestRepairWithAO extends TestApiBaseRepair {
     
     @Test(groups={"slow"})
     public void testRepairCancelAO() throws Exception {
+        
+        log.info("Starting testRepairCancelAO");
+        
         String baseProduct = "Shotgun";
         BillingPeriod baseTerm = BillingPeriod.MONTHLY;
         String basePriceList = PriceListSet.DEFAULT_PRICELIST_NAME;
@@ -472,13 +491,14 @@ public class TestRepairWithAO extends TestApiBaseRepair {
         SubscriptionData baseSubscription = createSubscription(baseProduct, baseTerm, basePriceList);
 
         // MOVE CLOCK A LITTLE BIT-- STILL IN TRIAL
-        Duration someTimeLater = getDurationDay(3);
-        clock.setDeltaFromReality(someTimeLater, DAY_IN_MS);
+        Interval it = new Interval(clock.getUTCNow(), clock.getUTCNow().plusDays(4));
+        clock.addDeltaFromReality(it.toDurationMillis());
 
         SubscriptionData aoSubscription = createSubscription("Telescopic-Scope", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME);
 
         // MOVE CLOCK A LITTLE BIT MORE -- STILL IN TRIAL
-        clock.addDeltaFromReality(someTimeLater);
+        it = new Interval(clock.getUTCNow(), clock.getUTCNow().plusDays(3));
+        clock.addDeltaFromReality(it.toDurationMillis());
 
         BundleTimeline bundleRepair = repairApi.getBundleRepair(bundle.getId());
         sortEventsOnBundle(bundleRepair);
@@ -530,7 +550,10 @@ public class TestRepairWithAO extends TestApiBaseRepair {
         assertEquals(newBaseSubscription.getActiveVersion(), SubscriptionEvents.INITIAL_VERSION);
         
         dryRun = false;
+        testListener.pushExpectedEvent(NextEvent.REPAIR_BUNDLE);
         BundleTimeline realRunBundleRepair = repairApi.repairBundle(bRepair, dryRun, context);
+        assertTrue(testListener.isCompleted(5000));
+
         
         aoRepair = getSubscriptionRepair(aoSubscription.getId(), realRunBundleRepair);
         assertEquals(aoRepair.getExistingEvents().size(), 2);
@@ -553,6 +576,9 @@ public class TestRepairWithAO extends TestApiBaseRepair {
     
     @Test(groups={"slow"})
     public void testRepairRecreateAO() throws Exception {
+        
+        log.info("Starting testRepairRecreateAO");
+        
         String baseProduct = "Shotgun";
         BillingPeriod baseTerm = BillingPeriod.MONTHLY;
         String basePriceList = PriceListSet.DEFAULT_PRICELIST_NAME;
@@ -561,13 +587,14 @@ public class TestRepairWithAO extends TestApiBaseRepair {
         SubscriptionData baseSubscription = createSubscription(baseProduct, baseTerm, basePriceList);
 
         // MOVE CLOCK A LITTLE BIT-- STILL IN TRIAL
-        Duration someTimeLater = getDurationDay(3);
-        clock.setDeltaFromReality(someTimeLater, DAY_IN_MS);
+        Interval it = new Interval(clock.getUTCNow(), clock.getUTCNow().plusDays(4));
+        clock.addDeltaFromReality(it.toDurationMillis());
 
         SubscriptionData aoSubscription = createSubscription("Telescopic-Scope", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME);
 
         // MOVE CLOCK A LITTLE BIT MORE -- STILL IN TRIAL
-        clock.addDeltaFromReality(someTimeLater);
+        it = new Interval(clock.getUTCNow(), clock.getUTCNow().plusDays(3));
+        clock.addDeltaFromReality(it.toDurationMillis());
 
         BundleTimeline bundleRepair = repairApi.getBundleRepair(bundle.getId());
         sortEventsOnBundle(bundleRepair);
@@ -616,7 +643,9 @@ public class TestRepairWithAO extends TestApiBaseRepair {
         
         // NOW COMMIT
         dryRun = false;
+        testListener.pushExpectedEvent(NextEvent.REPAIR_BUNDLE);
         BundleTimeline realRunBundleRepair = repairApi.repairBundle(bRepair, dryRun, context);
+        assertTrue(testListener.isCompleted(5000));
         
         aoRepair = getSubscriptionRepair(aoSubscription.getId(), realRunBundleRepair);
         assertEquals(aoRepair.getExistingEvents().size(), 2);
@@ -644,6 +673,8 @@ public class TestRepairWithAO extends TestApiBaseRepair {
     @Test(groups={"slow"})
     public void testRepairChangeAOOK() throws Exception {
         
+        log.info("Starting testRepairChangeAOOK");
+        
         String baseProduct = "Shotgun";
         BillingPeriod baseTerm = BillingPeriod.MONTHLY;
         String basePriceList = PriceListSet.DEFAULT_PRICELIST_NAME;
@@ -652,13 +683,14 @@ public class TestRepairWithAO extends TestApiBaseRepair {
         SubscriptionData baseSubscription = createSubscription(baseProduct, baseTerm, basePriceList);
 
         // MOVE CLOCK A LITTLE BIT-- STILL IN TRIAL
-        Duration someTimeLater = getDurationDay(3);
-        clock.setDeltaFromReality(someTimeLater, DAY_IN_MS);
+        Interval it = new Interval(clock.getUTCNow(), clock.getUTCNow().plusDays(4));
+        clock.addDeltaFromReality(it.toDurationMillis());
 
         SubscriptionData aoSubscription = createSubscription("Telescopic-Scope", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME);
 
         // MOVE CLOCK A LITTLE BIT MORE -- STILL IN TRIAL
-        clock.addDeltaFromReality(someTimeLater);
+        it = new Interval(clock.getUTCNow(), clock.getUTCNow().plusDays(3));
+        clock.addDeltaFromReality(it.toDurationMillis());
         
         BundleTimeline bundleRepair = repairApi.getBundleRepair(bundle.getId());
         sortEventsOnBundle(bundleRepair);
@@ -706,7 +738,9 @@ public class TestRepairWithAO extends TestApiBaseRepair {
         
         // AND NOW COMMIT
         dryRun = false;
+        testListener.pushExpectedEvent(NextEvent.REPAIR_BUNDLE);
         BundleTimeline realRunBundleRepair = repairApi.repairBundle(bRepair, dryRun, context);
+        assertTrue(testListener.isCompleted(5000));
         
         aoRepair = getSubscriptionRepair(aoSubscription.getId(), realRunBundleRepair);
         assertEquals(aoRepair.getExistingEvents().size(), 3);
@@ -734,10 +768,11 @@ public class TestRepairWithAO extends TestApiBaseRepair {
         assertNotNull(currentPhase);
         assertEquals(currentPhase.getPhaseType(), PhaseType.DISCOUNT);
         
-        testListener.pushNextApiExpectedEvent(NextEvent.PHASE);
-        someTimeLater = getDurationDay(60);
-        clock.addDeltaFromReality(someTimeLater);
-        assertTrue(testListener.isApiCompleted(5000));
+        testListener.pushExpectedEvent(NextEvent.PHASE);
+        
+        it = new Interval(clock.getUTCNow(), clock.getUTCNow().plusDays(60));
+        clock.addDeltaFromReality(it.toDurationMillis());
+        assertTrue(testListener.isCompleted(5000));
         
         newAoSubscription = (SubscriptionData)  entitlementApi.getSubscriptionFromId(aoSubscription.getId());
         currentPhase = newAoSubscription.getCurrentPhase();
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 285b17d..54fda89 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
@@ -24,6 +24,7 @@ import java.util.List;
 import java.util.UUID;
 
 import org.joda.time.DateTime;
+import org.joda.time.Interval;
 import org.testng.annotations.BeforeMethod;
 import org.testng.annotations.Test;
 
@@ -31,22 +32,17 @@ import com.google.inject.Guice;
 import com.google.inject.Injector;
 import com.google.inject.Stage;
 import com.ning.billing.ErrorCode;
+import com.ning.billing.api.TestApiListener.NextEvent;
 import com.ning.billing.catalog.api.BillingPeriod;
-import com.ning.billing.catalog.api.Duration;
 import com.ning.billing.catalog.api.PhaseType;
 import com.ning.billing.catalog.api.PlanPhaseSpecifier;
 import com.ning.billing.catalog.api.PriceListSet;
 import com.ning.billing.catalog.api.ProductCategory;
 import com.ning.billing.entitlement.api.SubscriptionTransitionType;
-import com.ning.billing.entitlement.api.ApiTestListener.NextEvent;
-import com.ning.billing.entitlement.api.timeline.BundleTimeline;
-import com.ning.billing.entitlement.api.timeline.EntitlementRepairException;
-import com.ning.billing.entitlement.api.timeline.SubscriptionTimeline;
 import com.ning.billing.entitlement.api.timeline.SubscriptionTimeline.DeletedEvent;
 import com.ning.billing.entitlement.api.timeline.SubscriptionTimeline.NewEvent;
 import com.ning.billing.entitlement.api.user.EntitlementUserApiException;
 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.glue.MockEngineModuleMemory;
 
@@ -61,27 +57,30 @@ public class TestRepairWithError extends TestApiBaseRepair {
         return Guice.createInjector(Stage.DEVELOPMENT, new MockEngineModuleMemory());
     }
 
-
-    @BeforeMethod(groups={"fast"})
-    public void beforeMethod() throws Exception {
+    @BeforeMethod(alwaysRun = true)
+    public void setupTest() throws Exception {
+        super.setupTest();
         test = new TestWithException();
         startDate = clock.getUTCNow();
         baseSubscription = createSubscription(baseProduct, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, startDate);
-        testListener.reset();
-        clock.resetDeltaFromReality();
     }
   
     @Test(groups={"fast"})
     public void testENT_REPAIR_NEW_EVENT_BEFORE_LAST_BP_REMAINING() throws Exception {
+        
+        log.info("Starting testENT_REPAIR_NEW_EVENT_BEFORE_LAST_BP_REMAINING");
+        
         test.withException(new TestWithExceptionCallback() {
             @Override
             public void doTest() throws EntitlementRepairException {
 
                 // MOVE AFTER TRIAL
-                testListener.pushNextApiExpectedEvent(NextEvent.PHASE);
-                Duration durationShift = getDurationDay(40);
-                clock.setDeltaFromReality(durationShift, 0);
-                assertTrue(testListener.isApiCompleted(5000));
+                testListener.pushExpectedEvent(NextEvent.PHASE);
+                
+                Interval it = new Interval(clock.getUTCNow(), clock.getUTCNow().plusDays(40));
+                clock.addDeltaFromReality(it.toDurationMillis());
+
+                assertTrue(testListener.isCompleted(5000));
                 
                 BundleTimeline bundleRepair = repairApi.getBundleRepair(bundle.getId());
                 sortEventsOnBundle(bundleRepair);
@@ -99,23 +98,26 @@ public class TestRepairWithError extends TestApiBaseRepair {
     
     @Test(groups={"fast"})
     public void testENT_REPAIR_INVALID_DELETE_SET() throws Exception {
+        
+        log.info("Starting testENT_REPAIR_INVALID_DELETE_SET");
+        
         test.withException(new TestWithExceptionCallback() {
             @Override
             public void doTest() throws EntitlementRepairException, EntitlementUserApiException {
 
-                Duration durationShift = getDurationDay(3);
-                clock.setDeltaFromReality(durationShift, 0);
+                Interval it = new Interval(clock.getUTCNow(), clock.getUTCNow().plusDays(3));
+                clock.addDeltaFromReality(it.toDurationMillis());
                 
-                testListener.pushNextApiExpectedEvent(NextEvent.CHANGE);
+                testListener.pushExpectedEvent(NextEvent.CHANGE);
                 DateTime changeTime = clock.getUTCNow();
                 baseSubscription.changePlan("Assault-Rifle", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, changeTime, context);
-                assertTrue(testListener.isApiCompleted(5000));
+                assertTrue(testListener.isCompleted(5000));
                 
                 // MOVE AFTER TRIAL
-                testListener.pushNextApiExpectedEvent(NextEvent.PHASE);
-                durationShift = getDurationDay(40);
-                clock.addDeltaFromReality(durationShift);
-                assertTrue(testListener.isApiCompleted(5000));
+                testListener.pushExpectedEvent(NextEvent.PHASE);
+                it = new Interval(clock.getUTCNow(), clock.getUTCNow().plusDays(40));
+                clock.addDeltaFromReality(it.toDurationMillis());
+                assertTrue(testListener.isCompleted(5000));
                 
                 BundleTimeline bundleRepair = repairApi.getBundleRepair(bundle.getId());
                 sortEventsOnBundle(bundleRepair);
@@ -133,6 +135,9 @@ public class TestRepairWithError extends TestApiBaseRepair {
 
     @Test(groups={"fast"})
     public void testENT_REPAIR_NON_EXISTENT_DELETE_EVENT() throws Exception {
+        
+        log.info("Starting testENT_REPAIR_NON_EXISTENT_DELETE_EVENT");
+        
         test.withException(new TestWithExceptionCallback() {
             @Override
             public void doTest() throws EntitlementRepairException {
@@ -153,15 +158,18 @@ public class TestRepairWithError extends TestApiBaseRepair {
     
     @Test(groups={"fast"})
     public void testENT_REPAIR_SUB_RECREATE_NOT_EMPTY() throws Exception {
+        
+        log.info("Starting testENT_REPAIR_SUB_RECREATE_NOT_EMPTY");
+        
         test.withException(new TestWithExceptionCallback() {
             @Override
             public void doTest() throws EntitlementRepairException {
                 
                 // MOVE AFTER TRIAL
-                   testListener.pushNextApiExpectedEvent(NextEvent.PHASE);
-                   Duration durationShift = getDurationDay(40);
-                   clock.setDeltaFromReality(durationShift, 0);
-                   assertTrue(testListener.isApiCompleted(5000));
+                   testListener.pushExpectedEvent(NextEvent.PHASE);
+                   Interval it = new Interval(clock.getUTCNow(), clock.getUTCNow().plusDays(40));
+                   clock.addDeltaFromReality(it.toDurationMillis());
+                   assertTrue(testListener.isCompleted(5000));
                    
                    BundleTimeline bundleRepair = repairApi.getBundleRepair(bundle.getId());
                    sortEventsOnBundle(bundleRepair);
@@ -181,15 +189,19 @@ public class TestRepairWithError extends TestApiBaseRepair {
 
     @Test(groups={"fast"})
     public void testENT_REPAIR_SUB_EMPTY() throws Exception {
+
+        log.info("Starting testENT_REPAIR_SUB_EMPTY");
+        
         test.withException(new TestWithExceptionCallback() {
+
             @Override
             public void doTest() throws EntitlementRepairException {
                 
              // MOVE AFTER TRIAL
-                testListener.pushNextApiExpectedEvent(NextEvent.PHASE);
-                Duration durationShift = getDurationDay(40);
-                clock.setDeltaFromReality(durationShift, 0);
-                assertTrue(testListener.isApiCompleted(5000));
+                testListener.pushExpectedEvent(NextEvent.PHASE);
+                Interval it = new Interval(clock.getUTCNow(), clock.getUTCNow().plusDays(40));
+                clock.addDeltaFromReality(it.toDurationMillis());
+                assertTrue(testListener.isCompleted(5000));
                 
                 BundleTimeline bundleRepair = repairApi.getBundleRepair(bundle.getId());
                 sortEventsOnBundle(bundleRepair);
@@ -209,19 +221,22 @@ public class TestRepairWithError extends TestApiBaseRepair {
     
     @Test(groups={"fast"})
     public void testENT_REPAIR_AO_CREATE_BEFORE_BP_START() throws Exception {
+        
+        log.info("Starting testENT_REPAIR_AO_CREATE_BEFORE_BP_START");
+        
         test.withException(new TestWithExceptionCallback() {
             @Override
             public void doTest() throws EntitlementRepairException, EntitlementUserApiException {
                
 
                 // MOVE CLOCK A LITTLE BIT-- STILL IN TRIAL
-                Duration someTimeLater = getDurationDay(3);
-                clock.setDeltaFromReality(someTimeLater, DAY_IN_MS);
-
+                Interval it = new Interval(clock.getUTCNow(), clock.getUTCNow().plusDays(4));
+                clock.addDeltaFromReality(it.toDurationMillis());
                 SubscriptionData aoSubscription = createSubscription("Telescopic-Scope", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME);
 
                 // MOVE CLOCK A LITTLE BIT MORE -- STILL IN TRIAL
-                clock.addDeltaFromReality(someTimeLater);
+                it = new Interval(clock.getUTCNow(), clock.getUTCNow().plusDays(4));
+                clock.addDeltaFromReality(it.toDurationMillis());
 
                 BundleTimeline bundleRepair = repairApi.getBundleRepair(bundle.getId());
                 sortEventsOnBundle(bundleRepair);
@@ -254,19 +269,22 @@ public class TestRepairWithError extends TestApiBaseRepair {
     
     @Test(groups={"fast"})
     public void testENT_REPAIR_NEW_EVENT_BEFORE_LAST_AO_REMAINING() throws Exception {
+        
+        log.info("Starting testENT_REPAIR_NEW_EVENT_BEFORE_LAST_AO_REMAINING");
+        
         test.withException(new TestWithExceptionCallback() {
             @Override
             public void doTest() throws EntitlementRepairException, EntitlementUserApiException {
                 
 
                 // MOVE CLOCK A LITTLE BIT-- STILL IN TRIAL
-                Duration someTimeLater = getDurationDay(3);
-                clock.setDeltaFromReality(someTimeLater, DAY_IN_MS);
-
+                Interval it = new Interval(clock.getUTCNow(), clock.getUTCNow().plusDays(4));
+                clock.addDeltaFromReality(it.toDurationMillis());
                 SubscriptionData aoSubscription = createSubscription("Telescopic-Scope", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME);
 
                 // MOVE CLOCK A LITTLE BIT MORE -- STILL IN TRIAL
-                clock.addDeltaFromReality(someTimeLater);
+                it = new Interval(clock.getUTCNow(), clock.getUTCNow().plusDays(4));
+                clock.addDeltaFromReality(it.toDurationMillis());
 
                 BundleTimeline bundleRepair = repairApi.getBundleRepair(bundle.getId());
                 sortEventsOnBundle(bundleRepair);
@@ -298,13 +316,17 @@ public class TestRepairWithError extends TestApiBaseRepair {
 
     @Test(groups={"fast"})
     public void testENT_REPAIR_BP_RECREATE_MISSING_AO() throws Exception {
+        
+        log.info("Starting testENT_REPAIR_BP_RECREATE_MISSING_AO");
+        
         test.withException(new TestWithExceptionCallback() {
             @Override
             public void doTest() throws EntitlementRepairException, EntitlementUserApiException {
 
               //testListener.pushExpectedEvent(NextEvent.PHASE);
 
-                clock.setDeltaFromReality(getDurationDay(5), 0);
+                Interval it = new Interval(clock.getUTCNow(), clock.getUTCNow().plusDays(4));
+                clock.addDeltaFromReality(it.toDurationMillis());
                 //assertTrue(testListener.isCompleted(5000));
 
                 SubscriptionData aoSubscription = createSubscription("Laser-Scope", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME);
@@ -337,14 +359,18 @@ public class TestRepairWithError extends TestApiBaseRepair {
     //
     @Test(groups={"fast"}, enabled=false)
     public void testENT_REPAIR_BP_RECREATE_MISSING_AO_CREATE() throws Exception {
+        
+        log.info("Starting testENT_REPAIR_BP_RECREATE_MISSING_AO_CREATE");
+        
         test.withException(new TestWithExceptionCallback() {
             @Override
             public void doTest() throws EntitlementRepairException, EntitlementUserApiException {
                 /*
               //testListener.pushExpectedEvent(NextEvent.PHASE);
 
-                clock.setDeltaFromReality(getDurationDay(5), 0);
-                //assertTrue(testListener.isCompleted(5000));
+                Interval it = new Interval(clock.getUTCNow(), clock.getUTCNow().plusDays(4));
+                clock.addDeltaFromReality(it.toDurationMillis());
+
 
                 SubscriptionData aoSubscription = createSubscription("Laser-Scope", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME);
                 
@@ -382,6 +408,9 @@ public class TestRepairWithError extends TestApiBaseRepair {
     
     @Test(groups={"fast"}, enabled=false)
     public void testENT_REPAIR_MISSING_AO_DELETE_EVENT() throws Exception {
+        
+        log.info("Starting testENT_REPAIR_MISSING_AO_DELETE_EVENT");
+        
         test.withException(new TestWithExceptionCallback() {
             @Override
             public void doTest() throws EntitlementRepairException, EntitlementUserApiException {
@@ -389,6 +418,10 @@ public class TestRepairWithError extends TestApiBaseRepair {
                 
                 /*
                 // MOVE CLOCK -- JUST BEFORE END OF TRIAL
+                 *                 
+                Interval it = new Interval(clock.getUTCNow(), clock.getUTCNow().plusDays(29));
+                clock.addDeltaFromReality(it.toDurationMillis());
+
                 clock.setDeltaFromReality(getDurationDay(29), 0);
                 
                 SubscriptionData aoSubscription = createSubscription("Laser-Scope", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME);
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiAddOn.java b/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiAddOn.java
index 2780fe5..7990084 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiAddOn.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiAddOn.java
@@ -21,15 +21,17 @@ import static org.testng.Assert.assertNotNull;
 import static org.testng.Assert.assertNull;
 import static org.testng.Assert.assertTrue;
 
-import org.apache.commons.lang.NotImplementedException;
+import java.util.List;
+
 import org.joda.time.DateTime;
-import org.joda.time.Period;
+import org.joda.time.Interval;
 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.CatalogApiException;
 import com.ning.billing.catalog.api.Duration;
@@ -40,10 +42,9 @@ import com.ning.billing.catalog.api.PlanPhase;
 import com.ning.billing.catalog.api.PlanSpecifier;
 import com.ning.billing.catalog.api.PriceListSet;
 import com.ning.billing.catalog.api.ProductCategory;
-import com.ning.billing.catalog.api.TimeUnit;
-import com.ning.billing.entitlement.api.ApiTestListener.NextEvent;
 import com.ning.billing.entitlement.api.TestApiBase;
 import com.ning.billing.entitlement.api.user.Subscription.SubscriptionState;
+import com.ning.billing.entitlement.api.user.SubscriptionStatusDryRun.DryRunChangeReason;
 import com.ning.billing.entitlement.glue.MockEngineModuleSql;
 import com.ning.billing.util.clock.DefaultClock;
 
@@ -57,6 +58,8 @@ public class TestUserApiAddOn extends TestApiBase {
     @Test(enabled=true, groups={"slow"})
     public void testCreateCancelAddon() {
 
+        log.info("Starting testCreateCancelAddon");
+
         try {
             String baseProduct = "Shotgun";
             BillingPeriod baseTerm = BillingPeriod.MONTHLY;
@@ -75,11 +78,12 @@ public class TestUserApiAddOn extends TestApiBase {
             aoSubscription.cancel(now, false, context);
 
             testListener.reset();
-            testListener.pushNextApiExpectedEvent(NextEvent.CANCEL);
-            assertTrue(testListener.isApiCompleted(5000));
+            testListener.pushExpectedEvent(NextEvent.CANCEL);
+            assertTrue(testListener.isCompleted(5000));
 
             assertEquals(aoSubscription.getState(), SubscriptionState.CANCELLED);
 
+            assertListenerStatus();
         } catch (Exception e) {
             Assert.fail(e.getMessage());
         }
@@ -87,6 +91,9 @@ public class TestUserApiAddOn extends TestApiBase {
 
     @Test(enabled=true, groups={"slow"})
     public void testCancelBPWithAddon() {
+
+        log.info("Starting testCancelBPWithAddon");
+
         try {
 
             String baseProduct = "Shotgun";
@@ -103,13 +110,13 @@ public class TestUserApiAddOn extends TestApiBase {
             SubscriptionData aoSubscription = createSubscription(aoProduct, aoTerm, aoPriceList);
 
             testListener.reset();
-            testListener.pushNextApiExpectedEvent(NextEvent.PHASE);
-            testListener.pushNextApiExpectedEvent(NextEvent.PHASE);
+            testListener.pushExpectedEvent(NextEvent.PHASE);
+            testListener.pushExpectedEvent(NextEvent.PHASE);
 
             // MOVE CLOCK AFTER TRIAL + AO DISCOUNT
-            Duration twoMonths = getDurationMonth(2);
-            clock.setDeltaFromReality(twoMonths, DAY_IN_MS);
-            assertTrue(testListener.isApiCompleted(5000));
+            Interval it = new Interval(clock.getUTCNow(), clock.getUTCNow().plusMonths(2));
+            clock.addDeltaFromReality(it.toDurationMillis());
+            assertTrue(testListener.isCompleted(5000));
 
             // SET CTD TO CANCEL IN FUTURE
             DateTime now = clock.getUTCNow();
@@ -129,15 +136,19 @@ public class TestUserApiAddOn extends TestApiBase {
 
             // MOVE AFTER CANCELLATION
             testListener.reset();
-            testListener.pushNextApiExpectedEvent(NextEvent.CANCEL);
-            testListener.pushNextApiExpectedEvent(NextEvent.CANCEL);
-            clock.addDeltaFromReality(ctd);
-            assertTrue(testListener.isApiCompleted(5000));
+            testListener.pushExpectedEvent(NextEvent.CANCEL);
+            testListener.pushExpectedEvent(NextEvent.CANCEL);
+            
+            it = new Interval(clock.getUTCNow(), clock.getUTCNow().plusMonths(1));
+            clock.addDeltaFromReality(it.toDurationMillis());
+            assertTrue(testListener.isCompleted(5000));
 
             // REFETCH AO SUBSCRIPTION AND CHECK THIS IS CANCELLED
             aoSubscription = (SubscriptionData) entitlementApi.getSubscriptionFromId(aoSubscription.getId());
             assertEquals(aoSubscription.getState(), SubscriptionState.CANCELLED);
 
+            assertListenerStatus();
+
         } catch (Exception e) {
             Assert.fail(e.getMessage());
         }
@@ -145,7 +156,10 @@ public class TestUserApiAddOn extends TestApiBase {
 
 
     @Test(enabled=true, groups={"slow"})
-    public void testChangeBPWithAddonNonIncluded() {
+    public void testChangeBPWithAddonIncluded() {
+
+        log.info("Starting testChangeBPWithAddonIncluded");
+
         try {
 
             String baseProduct = "Shotgun";
@@ -162,13 +176,13 @@ public class TestUserApiAddOn extends TestApiBase {
             SubscriptionData aoSubscription = createSubscription(aoProduct, aoTerm, aoPriceList);
 
             testListener.reset();
-            testListener.pushNextApiExpectedEvent(NextEvent.PHASE);
-            testListener.pushNextApiExpectedEvent(NextEvent.PHASE);
+            testListener.pushExpectedEvent(NextEvent.PHASE);
+            testListener.pushExpectedEvent(NextEvent.PHASE);
 
             // MOVE CLOCK AFTER TRIAL + AO DISCOUNT
-            Duration twoMonths = getDurationMonth(2);
-            clock.setDeltaFromReality(twoMonths, DAY_IN_MS);
-            assertTrue(testListener.isApiCompleted(5000));
+            Interval it = new Interval(clock.getUTCNow(), clock.getUTCNow().plusMonths(2));
+            clock.addDeltaFromReality(it.toDurationMillis());
+            assertTrue(testListener.isCompleted(5000));
 
             // SET CTD TO CHANGE IN FUTURE
             DateTime now = clock.getUTCNow();
@@ -182,16 +196,26 @@ public class TestUserApiAddOn extends TestApiBase {
             BillingPeriod newBaseTerm = BillingPeriod.MONTHLY;
             String newBasePriceList = PriceListSet.DEFAULT_PRICELIST_NAME;
 
+            List<SubscriptionStatusDryRun> aoStatus = entitlementApi.getDryRunChangePlanStatus(baseSubscription.getId(), newBaseProduct, now);
+            assertEquals(aoStatus.size(), 1);
+            assertEquals(aoStatus.get(0).getId(), aoSubscription.getId());
+            assertEquals(aoStatus.get(0).getProductName(), aoProduct);
+            assertEquals(aoStatus.get(0).getBillingPeriod(), aoTerm);            
+            assertEquals(aoStatus.get(0).getPhaseType(), aoSubscription.getCurrentPhase().getPhaseType());                        
+            assertEquals(aoStatus.get(0).getPriceList(), aoSubscription.getCurrentPriceList().getName());
+            assertEquals(aoStatus.get(0).getReason(), DryRunChangeReason.AO_INCLUDED_IN_NEW_PLAN);            
+            
             testListener.reset();
-            testListener.pushNextApiExpectedEvent(NextEvent.CHANGE);
-            testListener.pushNextApiExpectedEvent(NextEvent.CANCEL);
+            testListener.pushExpectedEvent(NextEvent.CHANGE);
+            testListener.pushExpectedEvent(NextEvent.CANCEL);
             baseSubscription.changePlan(newBaseProduct, newBaseTerm, newBasePriceList, now, context);
-            assertTrue(testListener.isApiCompleted(5000));
+            assertTrue(testListener.isCompleted(5000));
 
             // REFETCH AO SUBSCRIPTION AND CHECK THIS CANCELLED
             aoSubscription = (SubscriptionData) entitlementApi.getSubscriptionFromId(aoSubscription.getId());
             assertEquals(aoSubscription.getState(), SubscriptionState.CANCELLED);
 
+            assertListenerStatus();
         } catch (Exception e) {
             Assert.fail(e.getMessage());
         }
@@ -199,6 +223,9 @@ public class TestUserApiAddOn extends TestApiBase {
 
     @Test(enabled=true, groups={"slow"})
     public void testChangeBPWithAddonNonAvailable() {
+
+        log.info("Starting testChangeBPWithAddonNonAvailable");
+
         try {
 
             String baseProduct = "Shotgun";
@@ -215,13 +242,13 @@ public class TestUserApiAddOn extends TestApiBase {
             SubscriptionData aoSubscription = createSubscription(aoProduct, aoTerm, aoPriceList);
 
             testListener.reset();
-            testListener.pushNextApiExpectedEvent(NextEvent.PHASE);
-            testListener.pushNextApiExpectedEvent(NextEvent.PHASE);
+            testListener.pushExpectedEvent(NextEvent.PHASE);
+            testListener.pushExpectedEvent(NextEvent.PHASE);
 
             // MOVE CLOCK AFTER TRIAL + AO DISCOUNT
-            Duration twoMonths = getDurationMonth(2);
-            clock.setDeltaFromReality(twoMonths, DAY_IN_MS);
-            assertTrue(testListener.isApiCompleted(5000));
+            Interval it = new Interval(clock.getUTCNow(), clock.getUTCNow().plusMonths(2));
+            clock.addDeltaFromReality(it.toDurationMillis());
+            assertTrue(testListener.isCompleted(5000));
 
             // SET CTD TO CANCEL IN FUTURE
             DateTime now = clock.getUTCNow();
@@ -235,9 +262,17 @@ public class TestUserApiAddOn extends TestApiBase {
             BillingPeriod newBaseTerm = BillingPeriod.MONTHLY;
             String newBasePriceList = PriceListSet.DEFAULT_PRICELIST_NAME;
 
+            List<SubscriptionStatusDryRun> aoStatus = entitlementApi.getDryRunChangePlanStatus(baseSubscription.getId(), newBaseProduct, now);
+            assertEquals(aoStatus.size(), 1);
+            assertEquals(aoStatus.get(0).getId(), aoSubscription.getId());
+            assertEquals(aoStatus.get(0).getProductName(), aoProduct);
+            assertEquals(aoStatus.get(0).getBillingPeriod(), aoTerm);   
+            assertEquals(aoStatus.get(0).getPhaseType(), aoSubscription.getCurrentPhase().getPhaseType());                                    
+            assertEquals(aoStatus.get(0).getPriceList(), aoSubscription.getCurrentPriceList().getName());
+            assertEquals(aoStatus.get(0).getReason(), DryRunChangeReason.AO_NOT_AVAILABLE_IN_NEW_PLAN);            
+            
             baseSubscription.changePlan(newBaseProduct, newBaseTerm, newBasePriceList, now, context);
 
-
             // REFETCH AO SUBSCRIPTION AND CHECK THIS IS ACTIVE
             aoSubscription = (SubscriptionData) entitlementApi.getSubscriptionFromId(aoSubscription.getId());
             assertEquals(aoSubscription.getState(), SubscriptionState.ACTIVE);
@@ -245,16 +280,18 @@ public class TestUserApiAddOn extends TestApiBase {
 
             // MOVE AFTER CHANGE
             testListener.reset();
-            testListener.pushNextApiExpectedEvent(NextEvent.CHANGE);
-            testListener.pushNextApiExpectedEvent(NextEvent.CANCEL);
-            clock.addDeltaFromReality(ctd);
-            assertTrue(testListener.isApiCompleted(5000));
+            testListener.pushExpectedEvent(NextEvent.CHANGE);
+            testListener.pushExpectedEvent(NextEvent.CANCEL);
+            it = new Interval(clock.getUTCNow(), clock.getUTCNow().plusMonths(1));
+            clock.addDeltaFromReality(it.toDurationMillis());
+            assertTrue(testListener.isCompleted(5000));
 
 
             // REFETCH AO SUBSCRIPTION AND CHECK THIS CANCELLED
             aoSubscription = (SubscriptionData) entitlementApi.getSubscriptionFromId(aoSubscription.getId());
             assertEquals(aoSubscription.getState(), SubscriptionState.CANCELLED);
 
+            assertListenerStatus();
         } catch (Exception e) {
             Assert.fail(e.getMessage());
         }
@@ -263,6 +300,9 @@ public class TestUserApiAddOn extends TestApiBase {
 
     @Test(enabled=true, groups={"slow"})
     public void testAddonCreateWithBundleAlign() {
+
+        log.info("Starting testAddonCreateWithBundleAlign");
+
         try {
             String aoProduct = "Telescopic-Scope";
             BillingPeriod aoTerm = BillingPeriod.MONTHLY;
@@ -278,6 +318,7 @@ public class TestUserApiAddOn extends TestApiBase {
 
             testAddonCreateInternal(aoProduct, aoTerm, aoPriceList, alignement);
 
+            assertListenerStatus();
         } catch (CatalogApiException e) {
             Assert.fail(e.getMessage());
         }
@@ -287,6 +328,8 @@ public class TestUserApiAddOn extends TestApiBase {
     @Test(enabled=true, groups={"slow"})
     public void testAddonCreateWithSubscriptionAlign() {
 
+        log.info("Starting testAddonCreateWithSubscriptionAlign");
+
         try {
             String aoProduct = "Laser-Scope";
             BillingPeriod aoTerm = BillingPeriod.MONTHLY;
@@ -302,13 +345,15 @@ public class TestUserApiAddOn extends TestApiBase {
 
             testAddonCreateInternal(aoProduct, aoTerm, aoPriceList, alignement);
 
-            } catch (CatalogApiException e) {
-                Assert.fail(e.getMessage());
-            }
+            assertListenerStatus();
+        } catch (CatalogApiException e) {
+            Assert.fail(e.getMessage());
+        }
     }
 
 
     private void testAddonCreateInternal(String aoProduct, BillingPeriod aoTerm, String aoPriceList, PlanAlignmentCreate expAlignement) {
+
         try {
 
             String baseProduct = "Shotgun";
@@ -319,9 +364,9 @@ public class TestUserApiAddOn extends TestApiBase {
             SubscriptionData baseSubscription = createSubscription(baseProduct, baseTerm, basePriceList);
 
             // MOVE CLOCK 14 DAYS LATER
-            Duration someTimeLater = getDurationDay(13);
-            clock.setDeltaFromReality(someTimeLater, DAY_IN_MS);
-
+            Interval it = new Interval(clock.getUTCNow(), clock.getUTCNow().plusDays(14));
+            clock.addDeltaFromReality(it.toDurationMillis());
+  
             // CREATE ADDON
             DateTime beforeAOCreation = clock.getUTCNow();
             SubscriptionData aoSubscription = createSubscription(aoProduct, aoTerm, aoPriceList);
@@ -338,73 +383,46 @@ public class TestUserApiAddOn extends TestApiBase {
             assertNotNull(aoCurrentPhase);
             assertEquals(aoCurrentPhase.getPhaseType(), PhaseType.DISCOUNT);
 
-           assertDateWithin(aoSubscription.getStartDate(), beforeAOCreation, afterAOCreation);
-           assertEquals(aoSubscription.getBundleStartDate(), baseSubscription.getBundleStartDate());
-
-           // CHECK next AO PHASE EVENT IS INDEED A MONTH AFTER BP STARTED => BUNDLE ALIGNMENT
-           SubscriptionEvent aoPendingTranstion = aoSubscription.getPendingTransition();
-
-           if (expAlignement == PlanAlignmentCreate.START_OF_BUNDLE) {
-               assertEquals(aoPendingTranstion.getEffectiveTransitionTime(), baseSubscription.getStartDate().plusMonths(1));
-           } else {
-               assertEquals(aoPendingTranstion.getEffectiveTransitionTime(), aoSubscription.getStartDate().plusMonths(1));
-           }
-
-           // ADD TWO PHASE EVENTS (BP + AO)
-           testListener.reset();
-           testListener.pushNextApiExpectedEvent(NextEvent.PHASE);
-           testListener.pushNextApiExpectedEvent(NextEvent.PHASE);
-
-           // MOVE THROUGH TIME TO GO INTO EVERGREEN
-           
-           // Talk with Stephane about this fix. It seemed that the add on phase change was not appearing in the queue
-           // hypothesis is that waiting a period that is exactly the duration of the phase might be an instant too short
-           // depending how the comparison works
-           
-           //someTimeLater = aoCurrentPhase.getDuration();
-           someTimeLater = new Duration() {
-                @Override
-                public TimeUnit getUnit() {
-                   return TimeUnit.DAYS;
-                }
-
-                @Override
-                public int getNumber() {
-                   return 32;
-                }
-
-                @Override
-                public DateTime addToDateTime(DateTime dateTime) {
-                   throw new NotImplementedException();
-                }
-                @Override
-                public Period toJodaPeriod() {
-                    throw new UnsupportedOperationException();
-                }
-           };
-           
-           clock.addDeltaFromReality(someTimeLater);
-           clock.addDeltaFromReality(getDurationDay(1));
-           assertTrue(testListener.isApiCompleted(5000));
-
-
-           // CHECK EVERYTHING AGAIN
-           aoSubscription = (SubscriptionData) entitlementApi.getSubscriptionFromId(aoSubscription.getId());
-
-           aoCurrentPlan = aoSubscription.getCurrentPlan();
-           assertNotNull(aoCurrentPlan);
-           assertEquals(aoCurrentPlan.getProduct().getName(),aoProduct);
-           assertEquals(aoCurrentPlan.getProduct().getCategory(), ProductCategory.ADD_ON);
-           assertEquals(aoCurrentPlan.getBillingPeriod(), aoTerm);
-
-           aoCurrentPhase = aoSubscription.getCurrentPhase();
-           assertNotNull(aoCurrentPhase);
-           assertEquals(aoCurrentPhase.getPhaseType(), PhaseType.EVERGREEN);
-
-
-           aoSubscription = (SubscriptionData) entitlementApi.getSubscriptionFromId(aoSubscription.getId());
-           aoPendingTranstion = aoSubscription.getPendingTransition();
-           assertNull(aoPendingTranstion);
+            assertDateWithin(aoSubscription.getStartDate(), beforeAOCreation, afterAOCreation);
+            assertEquals(aoSubscription.getBundleStartDate(), baseSubscription.getBundleStartDate());
+
+            // CHECK next AO PHASE EVENT IS INDEED A MONTH AFTER BP STARTED => BUNDLE ALIGNMENT
+            SubscriptionEvent aoPendingTranstion = aoSubscription.getPendingTransition();
+
+            if (expAlignement == PlanAlignmentCreate.START_OF_BUNDLE) {
+                assertEquals(aoPendingTranstion.getEffectiveTransitionTime(), baseSubscription.getStartDate().plusMonths(1));
+            } else {
+                assertEquals(aoPendingTranstion.getEffectiveTransitionTime(), aoSubscription.getStartDate().plusMonths(1));
+            }
+
+            // ADD TWO PHASE EVENTS (BP + AO)
+            testListener.reset();
+            testListener.pushExpectedEvent(NextEvent.PHASE);
+            testListener.pushExpectedEvent(NextEvent.PHASE);
+
+            // MOVE THROUGH TIME TO GO INTO EVERGREEN
+            it = new Interval(clock.getUTCNow(), clock.getUTCNow().plusDays(33));
+            clock.addDeltaFromReality(it.toDurationMillis());
+            assertTrue(testListener.isCompleted(5000));
+
+
+            // CHECK EVERYTHING AGAIN
+            aoSubscription = (SubscriptionData) entitlementApi.getSubscriptionFromId(aoSubscription.getId());
+
+            aoCurrentPlan = aoSubscription.getCurrentPlan();
+            assertNotNull(aoCurrentPlan);
+            assertEquals(aoCurrentPlan.getProduct().getName(),aoProduct);
+            assertEquals(aoCurrentPlan.getProduct().getCategory(), ProductCategory.ADD_ON);
+            assertEquals(aoCurrentPlan.getBillingPeriod(), aoTerm);
+
+            aoCurrentPhase = aoSubscription.getCurrentPhase();
+            assertNotNull(aoCurrentPhase);
+            assertEquals(aoCurrentPhase.getPhaseType(), PhaseType.EVERGREEN);
+
+
+            aoSubscription = (SubscriptionData) entitlementApi.getSubscriptionFromId(aoSubscription.getId());
+            aoPendingTranstion = aoSubscription.getPendingTransition();
+            assertNull(aoPendingTranstion);
 
         } catch (EntitlementUserApiException e) {
             Assert.fail(e.getMessage());
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiCancel.java b/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiCancel.java
index 09046f1..7fb06e0 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiCancel.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiCancel.java
@@ -22,15 +22,16 @@ import static org.testng.Assert.assertNull;
 import static org.testng.Assert.assertTrue;
 
 import org.joda.time.DateTime;
+import org.joda.time.Interval;
 import org.testng.Assert;
 
+import com.ning.billing.api.TestApiListener.NextEvent;
 import com.ning.billing.catalog.api.BillingPeriod;
 import com.ning.billing.catalog.api.Duration;
 import com.ning.billing.catalog.api.PhaseType;
 import com.ning.billing.catalog.api.Plan;
 import com.ning.billing.catalog.api.PlanPhase;
 import com.ning.billing.catalog.api.PriceListSet;
-import com.ning.billing.entitlement.api.ApiTestListener.NextEvent;
 import com.ning.billing.entitlement.api.TestApiBase;
 import com.ning.billing.entitlement.api.billing.EntitlementBillingApiException;
 import com.ning.billing.util.clock.DefaultClock;
@@ -55,20 +56,21 @@ public abstract class TestUserApiCancel extends TestApiBase {
             assertEquals(currentPhase.getPhaseType(), PhaseType.TRIAL);
 
             // ADVANCE TIME still in trial
-            Duration moveALittleInTime = getDurationDay(3);
-            clock.setDeltaFromReality(moveALittleInTime, 0);
+            Interval it = new Interval(clock.getUTCNow(), clock.getUTCNow().plusDays(3));
+            clock.addDeltaFromReality(it.toDurationMillis());
 
             DateTime future = clock.getUTCNow();
-            testListener.pushNextApiExpectedEvent(NextEvent.CANCEL);
+            testListener.pushExpectedEvent(NextEvent.CANCEL);
 
             // CANCEL in trial period to get IMM policy
             subscription.cancel(clock.getUTCNow(), false, context);
             currentPhase = subscription.getCurrentPhase();
-            testListener.isApiCompleted(1000);
+            testListener.isCompleted(3000);
 
             assertNull(currentPhase);
             checkNextPhaseChange(subscription, 0, null);
 
+            assertListenerStatus();
         } catch (EntitlementUserApiException e) {
             Assert.fail(e.getMessage());
         }
@@ -94,9 +96,11 @@ public abstract class TestUserApiCancel extends TestApiBase {
             checkNextPhaseChange(subscription, 1, expectedPhaseTrialChange);
 
             // MOVE TO NEXT PHASE
-            testListener.pushNextApiExpectedEvent(NextEvent.PHASE);
-            clock.setDeltaFromReality(trialPhase.getDuration(), DAY_IN_MS);
-            assertTrue(testListener.isApiCompleted(2000));
+            testListener.pushExpectedEvent(NextEvent.PHASE);
+            Interval it = new Interval(clock.getUTCNow(), clock.getUTCNow().plusDays(31));
+            clock.addDeltaFromReality(it.toDurationMillis());
+
+            assertTrue(testListener.isCompleted(5000));
             trialPhase = subscription.getCurrentPhase();
             assertEquals(trialPhase.getPhaseType(), PhaseType.EVERGREEN);
 
@@ -106,21 +110,25 @@ public abstract class TestUserApiCancel extends TestApiBase {
             billingApi.setChargedThroughDate(subscription.getId(), newChargedThroughDate, context);
             subscription = (SubscriptionData) entitlementApi.getSubscriptionFromId(subscription.getId());
 
-            testListener.pushNextApiExpectedEvent(NextEvent.CANCEL);
-
             // CANCEL
+            testListener.setNonExpectedMode();
+            testListener.pushExpectedEvent(NextEvent.CANCEL);
             subscription.cancel(clock.getUTCNow(), false, context);
-            assertFalse(testListener.isApiCompleted(2000));
+            assertFalse(testListener.isCompleted(3000));
+            testListener.reset();
 
             // MOVE TO EOT + RECHECK
-            clock.addDeltaFromReality(ctd);
+            testListener.pushExpectedEvent(NextEvent.CANCEL);
+            it = new Interval(clock.getUTCNow(), clock.getUTCNow().plusMonths(1));
+            clock.addDeltaFromReality(it.toDurationMillis());
             DateTime future = clock.getUTCNow();
-            assertTrue(testListener.isApiCompleted(2000));
+            assertTrue(testListener.isCompleted(5000));
 
             PlanPhase currentPhase = subscription.getCurrentPhase();
             assertNull(currentPhase);
             checkNextPhaseChange(subscription, 0, null);
 
+            assertListenerStatus();
         } catch (EntitlementUserApiException e) {
             Assert.fail(e.getMessage());
         }
@@ -147,22 +155,25 @@ public abstract class TestUserApiCancel extends TestApiBase {
             checkNextPhaseChange(subscription, 1, expectedPhaseTrialChange);
 
             // MOVE TO NEXT PHASE
-            testListener.pushNextApiExpectedEvent(NextEvent.PHASE);
-            clock.setDeltaFromReality(trialPhase.getDuration(), DAY_IN_MS);
-            assertTrue(testListener.isApiCompleted(2000));
+            testListener.pushExpectedEvent(NextEvent.PHASE);
+            
+            Interval it = new Interval(clock.getUTCNow(), clock.getUTCNow().plusDays(31));
+            clock.addDeltaFromReality(it.toDurationMillis());
+            assertTrue(testListener.isCompleted(5000));
             trialPhase = subscription.getCurrentPhase();
             assertEquals(trialPhase.getPhaseType(), PhaseType.EVERGREEN);
 
-            testListener.pushNextApiExpectedEvent(NextEvent.CANCEL);
+            testListener.pushExpectedEvent(NextEvent.CANCEL);
 
             // CANCEL
             subscription.cancel(clock.getUTCNow(), false, context);
-            assertTrue(testListener.isApiCompleted(2000));
+            assertTrue(testListener.isCompleted(5000));
 
             PlanPhase currentPhase = subscription.getCurrentPhase();
             assertNull(currentPhase);
             checkNextPhaseChange(subscription, 0, null);
 
+            assertListenerStatus();
         } catch (EntitlementUserApiException e) {
             Assert.fail(e.getMessage());
         }
@@ -191,9 +202,10 @@ public abstract class TestUserApiCancel extends TestApiBase {
             checkNextPhaseChange(subscription, 1, expectedPhaseTrialChange);
 
             // MOVE TO NEXT PHASE
-            testListener.pushNextApiExpectedEvent(NextEvent.PHASE);
-            clock.setDeltaFromReality(trialPhase.getDuration(), DAY_IN_MS);
-            assertTrue(testListener.isApiCompleted(2000));
+            testListener.pushExpectedEvent(NextEvent.PHASE);
+            Interval it = new Interval(clock.getUTCNow(), clock.getUTCNow().plusDays(31));
+            clock.addDeltaFromReality(it.toDurationMillis());
+            assertTrue(testListener.isCompleted(5000));
             PlanPhase currentPhase = subscription.getCurrentPhase();
             assertEquals(currentPhase.getPhaseType(), PhaseType.EVERGREEN);
 
@@ -203,27 +215,25 @@ public abstract class TestUserApiCancel extends TestApiBase {
             billingApi.setChargedThroughDate(subscription.getId(), newChargedThroughDate, context);
             subscription = (SubscriptionData) entitlementApi.getSubscriptionFromId(subscription.getId());
 
-            testListener.pushNextApiExpectedEvent(NextEvent.CANCEL);
-
-            // CANCEL
+            // CANCEL EOT
             subscription.cancel(clock.getUTCNow(), false, context);
-            assertFalse(testListener.isApiCompleted(2000));
 
             subscription.uncancel(context);
-
+            
             // MOVE TO EOT + RECHECK
-            clock.addDeltaFromReality(ctd);
-            DateTime future = clock.getUTCNow();
-            assertFalse(testListener.isApiCompleted(2000));
+            testListener.pushExpectedEvent(NextEvent.UNCANCEL);            
+            it = new Interval(clock.getUTCNow(), clock.getUTCNow().plusMonths(1));
+            clock.addDeltaFromReality(it.toDurationMillis());
+            assertTrue(testListener.isCompleted(5000));
 
             Plan currentPlan = subscription.getCurrentPlan();
             assertEquals(currentPlan.getProduct().getName(), prod);
             currentPhase = subscription.getCurrentPhase();
             assertEquals(currentPhase.getPhaseType(), PhaseType.EVERGREEN);
 
+            assertListenerStatus();
         } catch (EntitlementUserApiException e) {
             Assert.fail(e.getMessage());
         }
     }
-
 }
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiCancelMemory.java b/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiCancelMemory.java
index 7eccbdd..625002a 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiCancelMemory.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiCancelMemory.java
@@ -33,25 +33,25 @@ public class TestUserApiCancelMemory extends TestUserApiCancel {
     }
 
     @Override
-    @Test(enabled=true, groups={"fast-disabled"})
+    @Test(enabled=true, groups={"fast"})
     public void testCancelSubscriptionIMM() {
         super.testCancelSubscriptionIMM();
     }
 
     @Override
-    @Test(enabled=true, groups={"fast-disabled"})
+    @Test(enabled=true, groups={"fast"})
     public void testCancelSubscriptionEOTWithChargeThroughDate() throws EntitlementBillingApiException {
         super.testCancelSubscriptionEOTWithChargeThroughDate();
     }
 
     @Override
-    @Test(enabled=true, groups={"fast-disabled"})
+    @Test(enabled=true, groups={"fast"})
     public void testCancelSubscriptionEOTWithNoChargeThroughDate() {
         super.testCancelSubscriptionEOTWithNoChargeThroughDate();
     }
 
     @Override
-    @Test(enabled=true, groups={"fast-disabled"})
+    @Test(enabled=true, groups={"fast"})
     public void testUncancel() throws EntitlementBillingApiException {
         super.testUncancel();
     }
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiChangePlan.java b/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiChangePlan.java
index b5b98e4..b1d2c0b 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiChangePlan.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiChangePlan.java
@@ -25,8 +25,10 @@ import java.util.ArrayList;
 import java.util.List;
 
 import org.joda.time.DateTime;
+import org.joda.time.Interval;
 import org.testng.Assert;
 
+import com.ning.billing.api.TestApiListener.NextEvent;
 import com.ning.billing.catalog.api.BillingPeriod;
 import com.ning.billing.catalog.api.Duration;
 import com.ning.billing.catalog.api.PhaseType;
@@ -34,7 +36,6 @@ import com.ning.billing.catalog.api.Plan;
 import com.ning.billing.catalog.api.PlanPhase;
 import com.ning.billing.catalog.api.PriceListSet;
 import com.ning.billing.catalog.api.ProductCategory;
-import com.ning.billing.entitlement.api.ApiTestListener.NextEvent;
 import com.ning.billing.entitlement.api.TestApiBase;
 import com.ning.billing.entitlement.api.billing.EntitlementBillingApiException;
 import com.ning.billing.entitlement.events.EntitlementEvent;
@@ -69,7 +70,7 @@ public abstract class TestUserApiChangePlan extends TestApiBase {
     private void tChangePlanBundleAlignEOTWithNoChargeThroughDate(String fromProd, BillingPeriod fromTerm, String fromPlanSet,
         String toProd, BillingPeriod toTerm, String toPlanSet) {
 
-        log.info("Starting testChangePlanBundleAlignEOTWithNoChargeThroughDateReal");
+        log.info("Starting testChangePlanBundleAlignEOTWithNoChargeThroughDate");
 
         try {
 
@@ -78,22 +79,26 @@ public abstract class TestUserApiChangePlan extends TestApiBase {
 
             // MOVE TO NEXT PHASE
             PlanPhase currentPhase = subscription.getCurrentPhase();
-            testListener.pushNextApiExpectedEvent(NextEvent.PHASE);
-            clock.setDeltaFromReality(currentPhase.getDuration(), DAY_IN_MS);
+            testListener.pushExpectedEvent(NextEvent.PHASE);
+            
+            Interval it = new Interval(clock.getUTCNow(), clock.getUTCNow().plusDays(31));
+            clock.addDeltaFromReality(it.toDurationMillis());
+
             DateTime futureNow = clock.getUTCNow();
             DateTime nextExpectedPhaseChange = DefaultClock.addDuration(subscription.getStartDate(), currentPhase.getDuration());
             assertTrue(futureNow.isAfter(nextExpectedPhaseChange));
-            assertTrue(testListener.isApiCompleted(3000));
+            assertTrue(testListener.isCompleted(5000));
 
             // CHANGE PLAN
-            testListener.pushNextApiExpectedEvent(NextEvent.CHANGE);
+            testListener.pushExpectedEvent(NextEvent.CHANGE);
             subscription.changePlan(toProd, toTerm, toPlanSet, clock.getUTCNow(), context);
-            assertTrue(testListener.isApiCompleted(2000));
+            assertTrue(testListener.isCompleted(5000));
 
             // CHECK CHANGE PLAN
             currentPhase = subscription.getCurrentPhase();
             checkChangePlan(subscription, toProd, ProductCategory.BASE, toTerm, PhaseType.EVERGREEN);
 
+            assertListenerStatus();
         } catch (EntitlementUserApiException e) {
             Assert.fail(e.getMessage());
         }
@@ -101,13 +106,14 @@ public abstract class TestUserApiChangePlan extends TestApiBase {
 
 
     protected void testChangePlanBundleAlignEOTWithChargeThroughDate() throws EntitlementBillingApiException {
+        log.info("Starting testChangePlanBundleAlignEOTWithChargeThroughDate");
         testChangePlanBundleAlignEOTWithChargeThroughDate("Shotgun", BillingPeriod.ANNUAL, "gunclubDiscount", "Pistol", BillingPeriod.ANNUAL, "gunclubDiscount");
     }
 
     private void testChangePlanBundleAlignEOTWithChargeThroughDate(String fromProd, BillingPeriod fromTerm, String fromPlanSet,
             String toProd, BillingPeriod toTerm, String toPlanSet) throws EntitlementBillingApiException {
 
-        log.info("Starting testChangeSubscriptionEOTWithChargeThroughDate");
+        
         try {
 
             // CREATE
@@ -118,9 +124,10 @@ public abstract class TestUserApiChangePlan extends TestApiBase {
 
 
             // MOVE TO NEXT PHASE
-            testListener.pushNextApiExpectedEvent(NextEvent.PHASE);
-            clock.setDeltaFromReality(trialPhase.getDuration(), DAY_IN_MS);
-            assertTrue(testListener.isApiCompleted(2000));
+            testListener.pushExpectedEvent(NextEvent.PHASE);
+            Interval it = new Interval(clock.getUTCNow(), clock.getUTCNow().plusDays(31));
+            clock.addDeltaFromReality(it.toDurationMillis());
+            assertTrue(testListener.isCompleted(5000));
             PlanPhase currentPhase = subscription.getCurrentPhase();
             assertEquals(currentPhase.getPhaseType(), PhaseType.DISCOUNT);
 
@@ -131,10 +138,11 @@ public abstract class TestUserApiChangePlan extends TestApiBase {
             billingApi.setChargedThroughDate(subscription.getId(), newChargedThroughDate, context);
 
             // RE READ SUBSCRIPTION + CHANGE PLAN
-            testListener.pushNextApiExpectedEvent(NextEvent.CHANGE);
+            testListener.setNonExpectedMode();
+            testListener.pushExpectedEvent(NextEvent.CHANGE);
             subscription = (SubscriptionData) entitlementApi.getSubscriptionFromId(subscription.getId());
             subscription.changePlan(toProd, toTerm, toPlanSet, clock.getUTCNow(), context);
-            assertFalse(testListener.isApiCompleted(2000));
+            assertFalse(testListener.isCompleted(3000));
             testListener.reset();
 
             // CHECK CHANGE PLAN
@@ -151,14 +159,16 @@ public abstract class TestUserApiChangePlan extends TestApiBase {
 
 
             // MOVE TO EOT
-            testListener.pushNextApiExpectedEvent(NextEvent.CHANGE);
-            clock.addDeltaFromReality(ctd);
-            assertTrue(testListener.isApiCompleted(5000));
+            testListener.pushExpectedEvent(NextEvent.CHANGE);
+            it = new Interval(clock.getUTCNow(), clock.getUTCNow().plusMonths(1));
+            clock.addDeltaFromReality(it.toDurationMillis());
+            assertTrue(testListener.isCompleted(5000));
 
             subscription = (SubscriptionData) entitlementApi.getSubscriptionFromId(subscription.getId());
             currentPhase = subscription.getCurrentPhase();
             checkChangePlan(subscription, toProd, ProductCategory.BASE, toTerm, PhaseType.DISCOUNT);
 
+            assertListenerStatus();
         } catch (EntitlementUserApiException e) {
             Assert.fail(e.getMessage());
         }
@@ -179,37 +189,31 @@ public abstract class TestUserApiChangePlan extends TestApiBase {
 
             SubscriptionData subscription = createSubscription(fromProd, fromTerm, fromPlanSet);
 
-            testListener.pushNextApiExpectedEvent(NextEvent.CHANGE);
+            testListener.pushExpectedEvent(NextEvent.CHANGE);
 
-            Duration moveALittleInTime = getDurationDay(3);
-            clock.setDeltaFromReality(moveALittleInTime, 0);
+            Interval it = new Interval(clock.getUTCNow(), clock.getUTCNow().plusDays(3));
+            clock.addDeltaFromReality(it.toDurationMillis());
 
             // CHANGE PLAN IMM
             subscription.changePlan(toProd, toTerm, toPlanSet, clock.getUTCNow(), context);
             checkChangePlan(subscription, toProd, ProductCategory.BASE, toTerm, PhaseType.TRIAL);
 
-            assertTrue(testListener.isApiCompleted(2000));
+            assertTrue(testListener.isCompleted(5000));
 
             PlanPhase currentPhase = subscription.getCurrentPhase();
             DateTime nextExpectedPhaseChange = DefaultClock.addDuration(subscription.getStartDate(), currentPhase.getDuration());
             checkNextPhaseChange(subscription, 1, nextExpectedPhaseChange);
 
             // NEXT PHASE
-            testListener.pushNextApiExpectedEvent(NextEvent.PHASE);
-            clock.addDeltaFromReality(currentPhase.getDuration());
+            testListener.pushExpectedEvent(NextEvent.PHASE);
+            it = new Interval(clock.getUTCNow(), clock.getUTCNow().plusDays(30));
+            clock.addDeltaFromReality(it.toDurationMillis());
             DateTime futureNow = clock.getUTCNow();
 
-            /*
-            try {
-                Thread.sleep(1000 * 3000);
-            } catch (Exception e) {
-
-            }
-            */
-
             assertTrue(futureNow.isAfter(nextExpectedPhaseChange));
-            assertTrue(testListener.isApiCompleted(3000));
+            assertTrue(testListener.isCompleted(5000));
 
+            assertListenerStatus();
         } catch (EntitlementUserApiException e) {
             Assert.fail(e.getMessage());
         }
@@ -217,14 +221,13 @@ public abstract class TestUserApiChangePlan extends TestApiBase {
 
 
     protected void testChangePlanChangePlanAlignEOTWithChargeThroughDate() throws EntitlementBillingApiException {
+        log.info("Starting testChangePlanChangePlanAlignEOTWithChargeThroughDate");
         tChangePlanChangePlanAlignEOTWithChargeThroughDate("Shotgun", BillingPeriod.ANNUAL, PriceListSet.DEFAULT_PRICELIST_NAME, "Assault-Rifle", BillingPeriod.ANNUAL, "rescue");
     }
 
     private void tChangePlanChangePlanAlignEOTWithChargeThroughDate(String fromProd, BillingPeriod fromTerm, String fromPlanSet,
             String toProd, BillingPeriod toTerm, String toPlanSet) throws EntitlementBillingApiException {
 
-        log.info("Starting testChangePlanBundleAlignEOTWithChargeThroughDate");
-
         try {
 
             DateTime currentTime = clock.getUTCNow();
@@ -235,11 +238,12 @@ public abstract class TestUserApiChangePlan extends TestApiBase {
             assertEquals(trialPhase.getPhaseType(), PhaseType.TRIAL);
 
             // MOVE TO NEXT PHASE
-            testListener.pushNextApiExpectedEvent(NextEvent.PHASE);
+            testListener.pushExpectedEvent(NextEvent.PHASE);
             currentTime = clock.getUTCNow();
-            clock.setDeltaFromReality(trialPhase.getDuration(), DAY_IN_MS);
+            Interval it = new Interval(clock.getUTCNow(), clock.getUTCNow().plusDays(31));
+            clock.addDeltaFromReality(it.toDurationMillis());
             currentTime = clock.getUTCNow();
-            assertTrue(testListener.isApiCompleted(2000));
+            assertTrue(testListener.isCompleted(5000));
 
             // SET CTD
             Duration ctd = getDurationMonth(1);
@@ -254,19 +258,22 @@ public abstract class TestUserApiChangePlan extends TestApiBase {
 
             // CHANGE PLAN
             currentTime = clock.getUTCNow();
-
-            testListener.pushNextApiExpectedEvent(NextEvent.CHANGE);
             subscription.changePlan(toProd, toTerm, toPlanSet, clock.getUTCNow(), context);
 
             checkChangePlan(subscription, fromProd, ProductCategory.BASE, fromTerm, PhaseType.EVERGREEN);
 
             // CHECK CHANGE DID NOT KICK IN YET
-            assertFalse(testListener.isApiCompleted(2000));
+            testListener.setNonExpectedMode();
+            testListener.pushExpectedEvent(NextEvent.CHANGE);
+            assertFalse(testListener.isCompleted(3000));
+            testListener.reset();
 
             // MOVE TO AFTER CTD
-            clock.addDeltaFromReality(ctd);
+            testListener.pushExpectedEvent(NextEvent.CHANGE);
+            it = new Interval(clock.getUTCNow(), clock.getUTCNow().plusMonths(1));
+            clock.addDeltaFromReality(it.toDurationMillis());
             currentTime = clock.getUTCNow();
-            assertTrue(testListener.isApiCompleted(2000));
+            assertTrue(testListener.isCompleted(5000));
 
             // CHECK CORRECT PRODUCT, PHASE, PLAN SET
             String currentProduct =  subscription.getCurrentPlan().getProduct().getName();
@@ -276,22 +283,27 @@ public abstract class TestUserApiChangePlan extends TestApiBase {
             assertNotNull(currentPhase);
             assertEquals(currentPhase.getPhaseType(), PhaseType.DISCOUNT);
 
-            testListener.pushNextApiExpectedEvent(NextEvent.PHASE);
-
             // MOVE TIME ABOUT ONE MONTH BEFORE NEXT EXPECTED PHASE CHANGE
-            clock.addDeltaFromReality(getDurationMonth(11));
-
+            testListener.setNonExpectedMode();
+            testListener.pushExpectedEvent(NextEvent.PHASE);
+            it = new Interval(clock.getUTCNow(), clock.getUTCNow().plusMonths(11));
+            clock.addDeltaFromReality(it.toDurationMillis());
             currentTime = clock.getUTCNow();
-            assertFalse(testListener.isApiCompleted(2000));
+            assertFalse(testListener.isCompleted(3000));
+            testListener.reset();
 
             DateTime nextExpectedPhaseChange = DefaultClock.addDuration(newChargedThroughDate, currentPhase.getDuration());
             checkNextPhaseChange(subscription, 1, nextExpectedPhaseChange);
 
             // MOVE TIME RIGHT AFTER NEXT EXPECTED PHASE CHANGE
-            clock.addDeltaFromReality(getDurationMonth(1));
+            testListener.pushExpectedEvent(NextEvent.PHASE);
+            it = new Interval(clock.getUTCNow(), clock.getUTCNow().plusMonths(1));
+            clock.addDeltaFromReality(it.toDurationMillis());
+
             currentTime = clock.getUTCNow();
-            assertTrue(testListener.isApiCompleted(2000));
+            assertTrue(testListener.isCompleted(5000));
 
+            assertListenerStatus();
         } catch (EntitlementUserApiException e) {
             Assert.fail(e.getMessage());
         }
@@ -299,15 +311,18 @@ public abstract class TestUserApiChangePlan extends TestApiBase {
 
     protected void testMultipleChangeLastIMM() throws EntitlementBillingApiException {
 
+        log.info("Starting testMultipleChangeLastIMM");
         try {
             SubscriptionData subscription = createSubscription("Assault-Rifle", BillingPeriod.MONTHLY, "gunclubDiscount");
             PlanPhase trialPhase = subscription.getCurrentPhase();
             assertEquals(trialPhase.getPhaseType(), PhaseType.TRIAL);
 
             // MOVE TO NEXT PHASE
-            testListener.pushNextApiExpectedEvent(NextEvent.PHASE);
-            clock.setDeltaFromReality(trialPhase.getDuration(), DAY_IN_MS);
-            assertTrue(testListener.isApiCompleted(2000));
+            testListener.pushExpectedEvent(NextEvent.PHASE);
+            Interval it = new Interval(clock.getUTCNow(), clock.getUTCNow().plusDays(31));
+            clock.addDeltaFromReality(it.toDurationMillis());
+
+            assertTrue(testListener.isCompleted(5000));
 
             // SET CTD
             List<Duration> durationList = new ArrayList<Duration>();
@@ -320,14 +335,16 @@ public abstract class TestUserApiChangePlan extends TestApiBase {
             subscription = (SubscriptionData) entitlementApi.getSubscriptionFromId(subscription.getId());
 
             // CHANGE EOT
-            testListener.pushNextApiExpectedEvent(NextEvent.CHANGE);
+            testListener.setNonExpectedMode();
+            testListener.pushExpectedEvent(NextEvent.CHANGE);
             subscription.changePlan("Pistol", BillingPeriod.MONTHLY, "gunclubDiscount", clock.getUTCNow(), context);
-            assertFalse(testListener.isApiCompleted(2000));
+            assertFalse(testListener.isCompleted(3000));
+            testListener.reset();
 
             // CHANGE
-            testListener.pushNextApiExpectedEvent(NextEvent.CHANGE);
+            testListener.pushExpectedEvent(NextEvent.CHANGE);
             subscription.changePlan("Assault-Rifle", BillingPeriod.ANNUAL, "gunclubDiscount", clock.getUTCNow(), context);
-            assertFalse(testListener.isApiCompleted(2000));
+            assertTrue(testListener.isCompleted(5000));
 
             Plan currentPlan = subscription.getCurrentPlan();
             assertNotNull(currentPlan);
@@ -338,7 +355,8 @@ public abstract class TestUserApiChangePlan extends TestApiBase {
             PlanPhase currentPhase = subscription.getCurrentPhase();
             assertNotNull(currentPhase);
             assertEquals(currentPhase.getPhaseType(), PhaseType.DISCOUNT);
-
+            
+            assertListenerStatus();
         } catch (EntitlementUserApiException e) {
             Assert.fail(e.getMessage());
         }
@@ -346,15 +364,17 @@ public abstract class TestUserApiChangePlan extends TestApiBase {
 
     protected void testMultipleChangeLastEOT() throws EntitlementBillingApiException {
 
+        log.info("Starting testMultipleChangeLastEOT");
         try {
 
             SubscriptionData subscription = createSubscription("Assault-Rifle", BillingPeriod.ANNUAL, "gunclubDiscount");
             PlanPhase trialPhase = subscription.getCurrentPhase();
             assertEquals(trialPhase.getPhaseType(), PhaseType.TRIAL);
 
-            testListener.pushNextApiExpectedEvent(NextEvent.PHASE);
-            clock.setDeltaFromReality(trialPhase.getDuration(), DAY_IN_MS);
-            assertTrue(testListener.isApiCompleted(2000));
+            testListener.pushExpectedEvent(NextEvent.PHASE);
+            Interval it = new Interval(clock.getUTCNow(), clock.getUTCNow().plusDays(31));
+            clock.addDeltaFromReality(it.toDurationMillis());
+            assertTrue(testListener.isCompleted(5000));
 
             // SET CTD
             List<Duration> durationList = new ArrayList<Duration>();
@@ -366,15 +386,17 @@ public abstract class TestUserApiChangePlan extends TestApiBase {
             subscription = (SubscriptionData) entitlementApi.getSubscriptionFromId(subscription.getId());
 
             // CHANGE EOT
-            testListener.pushNextApiExpectedEvent(NextEvent.CHANGE);
+            testListener.setNonExpectedMode();
+            testListener.pushExpectedEvent(NextEvent.CHANGE);
             subscription.changePlan("Shotgun", BillingPeriod.MONTHLY, "gunclubDiscount", clock.getUTCNow(), context);
-            assertFalse(testListener.isApiCompleted(2000));
+            assertFalse(testListener.isCompleted(3000));
             testListener.reset();
 
             // CHANGE EOT
-            testListener.pushNextApiExpectedEvent(NextEvent.CHANGE);
+            testListener.setNonExpectedMode();
+            testListener.pushExpectedEvent(NextEvent.CHANGE);
             subscription.changePlan("Pistol", BillingPeriod.ANNUAL, "gunclubDiscount", clock.getUTCNow(), context);
-            assertFalse(testListener.isApiCompleted(2000));
+            assertFalse(testListener.isCompleted(3000));
             testListener.reset();
 
             // CHECK NO CHANGE OCCURED YET
@@ -389,9 +411,11 @@ public abstract class TestUserApiChangePlan extends TestApiBase {
             assertEquals(currentPhase.getPhaseType(), PhaseType.DISCOUNT);
 
             // ACTIVATE CHNAGE BY MOVING AFTER CTD
-            testListener.pushNextApiExpectedEvent(NextEvent.CHANGE);
-            clock.addDeltaFromReality(ctd);
-            assertTrue(testListener.isApiCompleted(3000));
+            testListener.pushExpectedEvent(NextEvent.CHANGE);
+            it = new Interval(clock.getUTCNow(), clock.getUTCNow().plusMonths(1));
+            clock.addDeltaFromReality(it.toDurationMillis());
+
+            assertTrue(testListener.isCompleted(5000));
 
             currentPlan = subscription.getCurrentPlan();
             assertNotNull(currentPlan);
@@ -406,9 +430,10 @@ public abstract class TestUserApiChangePlan extends TestApiBase {
 
 
             // MOVE TO NEXT PHASE
-            testListener.pushNextApiExpectedEvent(NextEvent.PHASE);
-            clock.addDeltaFromReality(currentPhase.getDuration());
-            assertTrue(testListener.isApiCompleted(3000));
+            testListener.pushExpectedEvent(NextEvent.PHASE);
+            it = new Interval(clock.getUTCNow(), clock.getUTCNow().plusMonths(6));
+            clock.addDeltaFromReality(it.toDurationMillis());
+            assertTrue(testListener.isCompleted(5000));
             subscription = (SubscriptionData) entitlementApi.getSubscriptionFromId(subscription.getId());
 
             currentPlan = subscription.getCurrentPlan();
@@ -421,7 +446,7 @@ public abstract class TestUserApiChangePlan extends TestApiBase {
             assertNotNull(currentPhase);
             assertEquals(currentPhase.getPhaseType(), PhaseType.EVERGREEN);
 
-
+            assertListenerStatus();
         } catch (EntitlementUserApiException e) {
             Assert.fail(e.getMessage());
         }
@@ -429,6 +454,9 @@ public abstract class TestUserApiChangePlan extends TestApiBase {
 
 
     protected void testCorrectPhaseAlignmentOnChange() {
+        
+        log.info("Starting testCorrectPhaseAlignmentOnChange");
+        
         try {
 
             SubscriptionData subscription = createSubscription("Shotgun", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME);
@@ -436,13 +464,15 @@ public abstract class TestUserApiChangePlan extends TestApiBase {
             assertEquals(trialPhase.getPhaseType(), PhaseType.TRIAL);
 
             // MOVE 2 DAYS AHEAD
-            clock.setDeltaFromReality(getDurationDay(1), DAY_IN_MS);
+            Interval it = new Interval(clock.getUTCNow(), clock.getUTCNow().plusDays(2));
+            clock.addDeltaFromReality(it.toDurationMillis());
+    
 
             // CHANGE IMMEDIATE TO A 3 PHASES PLAN
             testListener.reset();
-            testListener.pushNextApiExpectedEvent(NextEvent.CHANGE);
+            testListener.pushExpectedEvent(NextEvent.CHANGE);
             subscription.changePlan("Assault-Rifle", BillingPeriod.ANNUAL, "gunclubDiscount", clock.getUTCNow(), context);
-            assertTrue(testListener.isApiCompleted(3000));
+            assertTrue(testListener.isCompleted(5000));
             testListener.reset();
 
             // CHECK EVERYTHING LOOKS CORRECT
@@ -456,9 +486,11 @@ public abstract class TestUserApiChangePlan extends TestApiBase {
             assertEquals(trialPhase.getPhaseType(), PhaseType.TRIAL);
 
             // MOVE AFTER TRIAL PERIOD -> DISCOUNT
-            testListener.pushNextApiExpectedEvent(NextEvent.PHASE);
-            clock.addDeltaFromReality(trialPhase.getDuration());
-            assertTrue(testListener.isApiCompleted(3000));
+            testListener.pushExpectedEvent(NextEvent.PHASE);
+            it = new Interval(clock.getUTCNow(), clock.getUTCNow().plusDays(30));
+            clock.addDeltaFromReality(it.toDurationMillis());
+
+            assertTrue(testListener.isCompleted(5000));
 
             trialPhase = subscription.getCurrentPhase();
             assertEquals(trialPhase.getPhaseType(), PhaseType.DISCOUNT);
@@ -471,6 +503,7 @@ public abstract class TestUserApiChangePlan extends TestApiBase {
 
             assertEquals(nextPhaseEffectiveDate, expectedNextPhaseDate);
 
+            assertListenerStatus();
 
         } catch (EntitlementUserApiException e) {
             Assert.fail(e.getMessage());
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiChangePlanMemory.java b/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiChangePlanMemory.java
index 8bbdd2d..0da1169 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiChangePlanMemory.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiChangePlanMemory.java
@@ -33,38 +33,38 @@ public class TestUserApiChangePlanMemory extends TestUserApiChangePlan {
 
 
     @Override
-    @Test(enabled=true, groups={"fast-disabled"})
+    @Test(enabled=true, groups={"fast"})
     public void testChangePlanBundleAlignEOTWithNoChargeThroughDate() {
          super.testChangePlanBundleAlignEOTWithNoChargeThroughDate();
     }
 
     @Override
-    @Test(enabled=true, groups={"fast-disabled"})
+    @Test(enabled=true, groups={"fast"})
     public void testChangePlanBundleAlignEOTWithChargeThroughDate() throws EntitlementBillingApiException {
         super.testChangePlanBundleAlignEOTWithChargeThroughDate();
     }
 
     @Override
-    @Test(enabled=true, groups={"fast-disabled"})
+    @Test(enabled=true, groups={"fast"})
     public void testChangePlanBundleAlignIMM() {
         super.testChangePlanBundleAlignIMM();
     }
 
     @Override
-    @Test(enabled=true, groups={"fast-disabled"})
+    @Test(enabled=true, groups={"fast"})
     public void testMultipleChangeLastIMM() throws EntitlementBillingApiException {
         super.testMultipleChangeLastIMM();
     }
 
     @Override
-    @Test(enabled=true, groups={"fast-disabled"})
+    @Test(enabled=true, groups={"fast"})
     public void testMultipleChangeLastEOT() throws EntitlementBillingApiException {
         super.testMultipleChangeLastEOT();
     }
 
 
     @Override
-    @Test(enabled=true, groups={"fast-disabled"})
+    @Test(enabled=true, groups={"fast"})
     public void testCorrectPhaseAlignmentOnChange() {
         super.testCorrectPhaseAlignmentOnChange();
     }
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiChangePlanSql.java b/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiChangePlanSql.java
index 509b056..1039b85 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiChangePlanSql.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiChangePlanSql.java
@@ -33,7 +33,7 @@ public class TestUserApiChangePlanSql extends TestUserApiChangePlan {
         return Guice.createInjector(Stage.DEVELOPMENT, new MockEngineModuleSql());
     }
 
-    @Test(enabled= true, groups={"stress"})
+    @Test(enabled= false, groups={"stress"})
     public void stressTest() throws Exception {
         for (int i = 0; i < MAX_STRESS_ITERATIONS; i++) {
             cleanupTest();
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiCreate.java b/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiCreate.java
index ef4533d..6517044 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiCreate.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiCreate.java
@@ -24,17 +24,18 @@ import static org.testng.Assert.assertTrue;
 import java.util.List;
 
 import org.joda.time.DateTime;
+import org.joda.time.Interval;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.testng.Assert;
 
+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.PlanPhase;
 import com.ning.billing.catalog.api.PriceListSet;
 import com.ning.billing.catalog.api.ProductCategory;
-import com.ning.billing.entitlement.api.ApiTestListener.NextEvent;
 import com.ning.billing.entitlement.api.TestApiBase;
 import com.ning.billing.entitlement.events.EntitlementEvent;
 import com.ning.billing.entitlement.events.phase.PhaseEvent;
@@ -56,8 +57,8 @@ public abstract class TestUserApiCreate extends TestApiBase {
             String planSetName = PriceListSet.DEFAULT_PRICELIST_NAME;
 
 
-            testListener.pushNextApiExpectedEvent(NextEvent.PHASE);
-            testListener.pushNextApiExpectedEvent(NextEvent.CREATE);
+            testListener.pushExpectedEvent(NextEvent.PHASE);
+            testListener.pushExpectedEvent(NextEvent.CREATE);
 
 
             SubscriptionData subscription = (SubscriptionData) entitlementApi.createSubscription(bundle.getId(),
@@ -69,8 +70,10 @@ public abstract class TestUserApiCreate extends TestApiBase {
             assertEquals(subscription.getBundleId(), bundle.getId());
             assertEquals(subscription.getStartDate(), requestedDate);
 
-            assertTrue(testListener.isApiCompleted(5000));
+            assertTrue(testListener.isCompleted(5000));
 
+            assertListenerStatus();
+            
         } catch (EntitlementUserApiException e) {
         	log.error("Unexpected exception",e);
             Assert.fail(e.getMessage());
@@ -89,7 +92,7 @@ public abstract class TestUserApiCreate extends TestApiBase {
             BillingPeriod term = BillingPeriod.MONTHLY;
             String planSetName = PriceListSet.DEFAULT_PRICELIST_NAME;
 
-            testListener.pushNextApiExpectedEvent(NextEvent.CREATE);
+            testListener.pushExpectedEvent(NextEvent.CREATE);
 
             SubscriptionData subscription = (SubscriptionData) entitlementApi.createSubscription(bundle.getId(),
                     getProductSpecifier(productName, planSetName, term, PhaseType.EVERGREEN), clock.getUTCNow(), context);
@@ -111,6 +114,8 @@ public abstract class TestUserApiCreate extends TestApiBase {
             assertNotNull(currentPhase);
             assertEquals(currentPhase.getPhaseType(), PhaseType.EVERGREEN);
 
+            assertListenerStatus();
+            
         } catch (EntitlementUserApiException e) {
             Assert.fail(e.getMessage());
         }
@@ -127,7 +132,7 @@ public abstract class TestUserApiCreate extends TestApiBase {
             BillingPeriod term = BillingPeriod.MONTHLY;
             String planSetName = PriceListSet.DEFAULT_PRICELIST_NAME;
 
-            testListener.pushNextApiExpectedEvent(NextEvent.CREATE);
+            testListener.pushExpectedEvent(NextEvent.CREATE);
 
             SubscriptionData subscription = (SubscriptionData) entitlementApi.createSubscription(bundle.getId(),
                     getProductSpecifier(productName, planSetName, term, null),
@@ -149,7 +154,7 @@ public abstract class TestUserApiCreate extends TestApiBase {
             PlanPhase currentPhase = subscription.getCurrentPhase();
             assertNotNull(currentPhase);
             assertEquals(currentPhase.getPhaseType(), PhaseType.TRIAL);
-            assertTrue(testListener.isApiCompleted(5000));
+            assertTrue(testListener.isCompleted(5000));
 
             List<EntitlementEvent> events = dao.getPendingEventsForSubscription(subscription.getId());
             assertNotNull(events);
@@ -160,15 +165,16 @@ public abstract class TestUserApiCreate extends TestApiBase {
             DateTime nextExpectedPhaseChange = DefaultClock.addDuration(subscription.getStartDate(), currentPhase.getDuration());
             assertEquals(nextPhaseChange, nextExpectedPhaseChange);
 
-            testListener.pushNextApiExpectedEvent(NextEvent.PHASE);
-
-            clock.setDeltaFromReality(currentPhase.getDuration(), DAY_IN_MS);
+            testListener.pushExpectedEvent(NextEvent.PHASE);
+            Interval it = new Interval(clock.getUTCNow(), clock.getUTCNow().plusDays(31));
+            clock.addDeltaFromReality(it.toDurationMillis());
 
             DateTime futureNow = clock.getUTCNow();
             assertTrue(futureNow.isAfter(nextPhaseChange));
 
-            assertTrue(testListener.isApiCompleted(5000));
+            assertTrue(testListener.isCompleted(5000));
 
+            assertListenerStatus();
         } catch (EntitlementUserApiException e) {
             Assert.fail(e.getMessage());
         }
@@ -184,7 +190,7 @@ public abstract class TestUserApiCreate extends TestApiBase {
             BillingPeriod term = BillingPeriod.ANNUAL;
             String planSetName = "gunclubDiscount";
 
-            testListener.pushNextApiExpectedEvent(NextEvent.CREATE);
+            testListener.pushExpectedEvent(NextEvent.CREATE);
 
             // CREATE SUBSCRIPTION
             SubscriptionData subscription = (SubscriptionData) entitlementApi.createSubscription(bundle.getId(),
@@ -194,27 +200,30 @@ public abstract class TestUserApiCreate extends TestApiBase {
             PlanPhase currentPhase = subscription.getCurrentPhase();
             assertNotNull(currentPhase);
             assertEquals(currentPhase.getPhaseType(), PhaseType.TRIAL);
-            assertTrue(testListener.isApiCompleted(5000));
+            assertTrue(testListener.isCompleted(5000));
 
             // MOVE TO DISCOUNT PHASE
-            testListener.pushNextApiExpectedEvent(NextEvent.PHASE);
-            clock.setDeltaFromReality(currentPhase.getDuration(), DAY_IN_MS);
+            testListener.pushExpectedEvent(NextEvent.PHASE);
+            Interval it = new Interval(clock.getUTCNow(), clock.getUTCNow().plusDays(31));
+            clock.addDeltaFromReality(it.toDurationMillis());
+            assertTrue(testListener.isCompleted(5000));
             currentPhase = subscription.getCurrentPhase();
             assertNotNull(currentPhase);
             assertEquals(currentPhase.getPhaseType(), PhaseType.DISCOUNT);
-            assertTrue(testListener.isApiCompleted(2000));
+            
 
             // MOVE TO EVERGREEN PHASE + RE-READ SUBSCRIPTION
-            testListener.pushNextApiExpectedEvent(NextEvent.PHASE);
-            clock.addDeltaFromReality(currentPhase.getDuration());
-            assertTrue(testListener.isApiCompleted(2000));
+            testListener.pushExpectedEvent(NextEvent.PHASE);
+            it = new Interval(clock.getUTCNow(), clock.getUTCNow().plusYears(1));
+            clock.addDeltaFromReality(it.toDurationMillis());
+            assertTrue(testListener.isCompleted(5000));
 
             subscription = (SubscriptionData) entitlementApi.getSubscriptionFromId(subscription.getId());
             currentPhase = subscription.getCurrentPhase();
             assertNotNull(currentPhase);
             assertEquals(currentPhase.getPhaseType(), PhaseType.EVERGREEN);
 
-
+            assertListenerStatus();
         } catch (EntitlementUserApiException e) {
             Assert.fail(e.getMessage());
         }
@@ -229,17 +238,15 @@ public abstract class TestUserApiCreate extends TestApiBase {
             BillingPeriod term = BillingPeriod.ANNUAL;
             String planSetName = PriceListSet.DEFAULT_PRICELIST_NAME;
 
-            testListener.pushNextApiExpectedEvent(NextEvent.CREATE);
+            testListener.pushExpectedEvent(NextEvent.CREATE);
 
             SubscriptionData subscription = (SubscriptionData) entitlementApi.createSubscription(bundle.getId(),
                     getProductSpecifier(productName, planSetName, term, null), clock.getUTCNow(), context);
             assertNotNull(subscription);
 
+            assertListenerStatus();
         } catch (EntitlementUserApiException e) {
             Assert.fail(e.getMessage());
         }
     }
-
-
-
 }
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiCreateMemory.java b/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiCreateMemory.java
index 7987d87..e4969f1 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiCreateMemory.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiCreateMemory.java
@@ -32,31 +32,31 @@ public class TestUserApiCreateMemory extends TestUserApiCreate {
     }
 
     @Override
-    @Test(enabled=true, groups={"fast-disabled"})
+    @Test(enabled=true, groups={"fast"})
     public void testCreateWithRequestedDate() {
         super.testCreateWithRequestedDate();
     }
 
     @Override
-    @Test(enabled=true, groups={"fast-disabled"})
+    @Test(enabled=true, groups={"fast"})
     public void testCreateWithInitialPhase() {
         super.testSimpleSubscriptionThroughPhases();
     }
 
     @Override
-    @Test(enabled=true, groups={"fast-disabled"})
+    @Test(enabled=true, groups={"fast"})
     public void testSimpleCreateSubscription() {
         super.testSimpleCreateSubscription();
     }
 
     @Override
-    @Test(enabled=true, groups={"fast-disabled"})
+    @Test(enabled=true, groups={"fast"})
     protected void testSimpleSubscriptionThroughPhases() {
         super.testSimpleSubscriptionThroughPhases();
     }
 
     @Override
-    @Test(enabled=true, groups={"fast-disabled"})
+    @Test(enabled=true, groups={"fast"})
     protected void testSubscriptionWithAddOn() {
         super.testSubscriptionWithAddOn();
     }
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiDemos.java b/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiDemos.java
index fdd6e66..0973666 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiDemos.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiDemos.java
@@ -26,19 +26,20 @@ import java.util.List;
 import java.util.UUID;
 
 import org.joda.time.DateTime;
+import org.joda.time.Interval;
 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.Duration;
 import com.ning.billing.catalog.api.PhaseType;
 import com.ning.billing.catalog.api.Plan;
 import com.ning.billing.catalog.api.PlanPhase;
 import com.ning.billing.catalog.api.ProductCategory;
-import com.ning.billing.entitlement.api.ApiTestListener.NextEvent;
 import com.ning.billing.entitlement.api.TestApiBase;
 import com.ning.billing.entitlement.api.billing.EntitlementBillingApiException;
 import com.ning.billing.entitlement.glue.MockEngineModuleSql;
@@ -69,6 +70,7 @@ public class TestUserApiDemos extends TestApiBase {
     @Test(enabled=true, groups="demos")
     public void testDemo1() throws EntitlementBillingApiException {
 
+        log.info("Starting testSubscriptionWithAddOn");
         try {
             System.out.println("DEMO 1 START");
 
@@ -80,16 +82,17 @@ public class TestUserApiDemos extends TestApiBase {
             displayState(subscription.getId(), "STEP 1. CREATED SUBSCRIPTION");
 
             /* STEP 2. CHANGE PLAN WHILE IN TRIAL */
-            testListener.pushNextApiExpectedEvent(NextEvent.CHANGE);
+            testListener.pushExpectedEvent(NextEvent.CHANGE);
             subscription.changePlan("Assault-Rifle", BillingPeriod.ANNUAL, "gunclubDiscount", clock.getUTCNow(), context);
-            assertTrue(testListener.isApiCompleted(3000));
+            assertTrue(testListener.isCompleted(5000));
 
             displayState(subscription.getId(), "STEP 2. CHANGED PLAN WHILE IN TRIAL");
 
             /* STEP 3. MOVE TO DISCOUNT PHASE */
-            testListener.pushNextApiExpectedEvent(NextEvent.PHASE);
-            clock.setDeltaFromReality(trialPhase.getDuration(), DAY_IN_MS);
-            assertTrue(testListener.isApiCompleted(3000));
+            testListener.pushExpectedEvent(NextEvent.PHASE);
+            Interval it = new Interval(clock.getUTCNow(), clock.getUTCNow().plusDays(31));
+            clock.addDeltaFromReality(it.toDurationMillis());
+            assertTrue(testListener.isCompleted(5000));
 
             displayState(subscription.getId(), "STEP 3. MOVE TO DISCOUNT PHASE");
 
@@ -103,25 +106,28 @@ public class TestUserApiDemos extends TestApiBase {
             billingApi.setChargedThroughDate(subscription.getId(), newChargedThroughDate, context);
             subscription = (SubscriptionData) entitlementApi.getSubscriptionFromId(subscription.getId());
 
-            testListener.pushNextApiExpectedEvent(NextEvent.CHANGE);
+            testListener.setNonExpectedMode();
+            testListener.pushExpectedEvent(NextEvent.CHANGE);
             subscription.changePlan("Shotgun", BillingPeriod.ANNUAL, "gunclubDiscount", clock.getUTCNow(), context);
-            assertFalse(testListener.isApiCompleted(2000));
+            assertFalse(testListener.isCompleted(3000));
             testListener.reset();
 
             displayState(subscription.getId(), "STEP 4. SET CTD AND CHANGE PLAN EOT (Shotgun)");
 
             /* STEP 5. CHANGE AGAIN */
-            testListener.pushNextApiExpectedEvent(NextEvent.CHANGE);
+            testListener.setNonExpectedMode();
+            testListener.pushExpectedEvent(NextEvent.CHANGE);
             subscription.changePlan("Pistol", BillingPeriod.ANNUAL, "gunclubDiscount", clock.getUTCNow(), context);
-            assertFalse(testListener.isApiCompleted(2000));
+            assertFalse(testListener.isCompleted(3000));
             testListener.reset();
 
             displayState(subscription.getId(), "STEP 5. CHANGE AGAIN EOT (Pistol)");
 
             /* STEP 6. MOVE TO EOT AND CHECK CHANGE OCCURED */
-            testListener.pushNextApiExpectedEvent(NextEvent.CHANGE);
-            clock.addDeltaFromReality(ctd);
-            assertTrue(testListener.isApiCompleted(2000));
+            testListener.pushExpectedEvent(NextEvent.CHANGE);
+            it = new Interval(clock.getUTCNow(), clock.getUTCNow().plusMonths(1));
+            clock.addDeltaFromReality(it.toDurationMillis());
+            assertTrue(testListener.isCompleted(5000));
 
             Plan currentPlan = subscription.getCurrentPlan();
             assertNotNull(currentPlan);
@@ -136,9 +142,10 @@ public class TestUserApiDemos extends TestApiBase {
             displayState(subscription.getId(), "STEP 6. MOVE TO EOT");
 
             /* STEP 7.  MOVE TO NEXT PHASE */
-            testListener.pushNextApiExpectedEvent(NextEvent.PHASE);
-            clock.addDeltaFromReality(currentPhase.getDuration());
-            assertTrue(testListener.isApiCompleted(5000));
+            testListener.pushExpectedEvent(NextEvent.PHASE);
+            it = new Interval(clock.getUTCNow(), clock.getUTCNow().plusMonths(6));
+            clock.addDeltaFromReality(it.toDurationMillis());
+            assertTrue(testListener.isCompleted(5000));
             subscription = (SubscriptionData) entitlementApi.getSubscriptionFromId(subscription.getId());
 
             currentPlan = subscription.getCurrentPlan();
@@ -154,11 +161,12 @@ public class TestUserApiDemos extends TestApiBase {
             displayState(subscription.getId(), "STEP 7.  MOVE TO NEXT PHASE");
 
             /* STEP 8. CANCEL IMM (NO CTD) */
-            testListener.pushNextApiExpectedEvent(NextEvent.CANCEL);
+            testListener.pushExpectedEvent(NextEvent.CANCEL);
             subscription.cancel(clock.getUTCNow(), false, context);
 
             displayState(subscription.getId(), "STEP 8.  CANCELLATION");
 
+            assertListenerStatus();
         } catch (EntitlementUserApiException e) {
             Assert.fail(e.getMessage());
         }
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiError.java b/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiError.java
index c3f6061..c56e0e0 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiError.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiError.java
@@ -25,6 +25,7 @@ import java.util.UUID;
 import javax.annotation.Nullable;
 
 import org.joda.time.DateTime;
+import org.joda.time.Interval;
 import org.testng.Assert;
 import org.testng.annotations.Test;
 
@@ -32,11 +33,11 @@ import com.google.inject.Guice;
 import com.google.inject.Injector;
 import com.google.inject.Stage;
 import com.ning.billing.ErrorCode;
+import com.ning.billing.api.TestApiListener.NextEvent;
 import com.ning.billing.catalog.api.BillingPeriod;
 import com.ning.billing.catalog.api.Duration;
 import com.ning.billing.catalog.api.PlanPhase;
 import com.ning.billing.catalog.api.PriceListSet;
-import com.ning.billing.entitlement.api.ApiTestListener.NextEvent;
 import com.ning.billing.entitlement.api.TestApiBase;
 import com.ning.billing.entitlement.glue.MockEngineModuleMemory;
 import com.ning.billing.util.clock.DefaultClock;
@@ -52,6 +53,9 @@ public class TestUserApiError extends TestApiBase {
 
     @Test(enabled=true, groups={"fast"})
     public void testCreateSubscriptionBadCatalog() {
+        
+        log.info("Starting testCreateSubscriptionBadCatalog");
+        
         // WRONG PRODUCTS
         tCreateSubscriptionInternal(bundle.getId(), null, BillingPeriod.ANNUAL, PriceListSet.DEFAULT_PRICELIST_NAME, ErrorCode.CAT_NULL_PRODUCT_NAME);
         tCreateSubscriptionInternal(bundle.getId(), "Whatever", BillingPeriod.ANNUAL, PriceListSet.DEFAULT_PRICELIST_NAME, ErrorCode.CAT_NO_SUCH_PRODUCT);
@@ -68,16 +72,19 @@ public class TestUserApiError extends TestApiBase {
 
     @Test(enabled=true, groups={"fast"})
     public void testCreateSubscriptionNoBundle() {
+        log.info("Starting testCreateSubscriptionNoBundle");
         tCreateSubscriptionInternal(null, "Shotgun", BillingPeriod.ANNUAL, PriceListSet.DEFAULT_PRICELIST_NAME, ErrorCode.ENT_CREATE_NO_BUNDLE);
     }
 
     @Test(enabled=true, groups={"fast"})
     public void testCreateSubscriptionNoBP() {
+        log.info("Starting testCreateSubscriptionNoBP");
         tCreateSubscriptionInternal(bundle.getId(), "Telescopic-Scope", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, ErrorCode.ENT_CREATE_NO_BP);
     }
 
     @Test(enabled=true, groups={"fast"})
     public void testCreateSubscriptionBPExists() {
+        log.info("Starting testCreateSubscriptionBPExists");
         try {
             createSubscription("Shotgun", BillingPeriod.ANNUAL, PriceListSet.DEFAULT_PRICELIST_NAME);
             tCreateSubscriptionInternal(bundle.getId(), "Shotgun", BillingPeriod.ANNUAL, PriceListSet.DEFAULT_PRICELIST_NAME, ErrorCode.ENT_CREATE_BP_EXISTS);
@@ -89,6 +96,7 @@ public class TestUserApiError extends TestApiBase {
 
     @Test(enabled=true, groups={"fast"})
     public void testRecreateSubscriptionBPNotCancelled() {
+        log.info("Starting testRecreateSubscriptionBPNotCancelled");
         try {
             SubscriptionData subscription = createSubscription("Shotgun", BillingPeriod.ANNUAL, PriceListSet.DEFAULT_PRICELIST_NAME);
             try {
@@ -105,6 +113,7 @@ public class TestUserApiError extends TestApiBase {
 
     @Test(enabled=true, groups={"fast"})
     public void testCreateSubscriptionAddOnNotAvailable() {
+        log.info("Starting testCreateSubscriptionAddOnNotAvailable");
         try {
             UUID accountId = UUID.randomUUID();
             SubscriptionBundle aoBundle = entitlementApi.createBundleForAccount(accountId, "myAOBundle", context);
@@ -118,6 +127,7 @@ public class TestUserApiError extends TestApiBase {
 
     @Test(enabled=true, groups={"fast"})
     public void testCreateSubscriptionAddOnIncluded() {
+        log.info("Starting testCreateSubscriptionAddOnIncluded");
         try {
             UUID accountId = UUID.randomUUID();
             SubscriptionBundle aoBundle = entitlementApi.createBundleForAccount(accountId, "myAOBundle", context);
@@ -150,10 +160,11 @@ public class TestUserApiError extends TestApiBase {
 
     @Test(enabled=true, groups={"fast"})
     public void testChangeSubscriptionNonActive() {
+        log.info("Starting testChangeSubscriptionNonActive");
         try {
             Subscription subscription = createSubscription("Shotgun", BillingPeriod.ANNUAL, PriceListSet.DEFAULT_PRICELIST_NAME);
 
-            testListener.pushNextApiExpectedEvent(NextEvent.CANCEL);
+            testListener.pushExpectedEvent(NextEvent.CANCEL);
             subscription.cancel(clock.getUTCNow(), false, context);
             try {
                 subscription.changePlan("Pistol", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, clock.getUTCNow(), context);
@@ -174,15 +185,17 @@ public class TestUserApiError extends TestApiBase {
 
     @Test(enabled=true, groups={"fast"})
     public void testChangeSubscriptionFutureCancelled() {
+        log.info("Starting testChangeSubscriptionFutureCancelled");
         try {
             Subscription subscription = createSubscription("Shotgun", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME);
             PlanPhase trialPhase = subscription.getCurrentPhase();
 
             // MOVE TO NEXT PHASE
             PlanPhase currentPhase = subscription.getCurrentPhase();
-            testListener.pushNextApiExpectedEvent(NextEvent.PHASE);
-            clock.setDeltaFromReality(currentPhase.getDuration(), DAY_IN_MS);
-            assertTrue(testListener.isApiCompleted(3000));
+            testListener.pushExpectedEvent(NextEvent.PHASE);
+            Interval it = new Interval(clock.getUTCNow(), clock.getUTCNow().plusDays(31));
+            clock.addDeltaFromReality(it.toDurationMillis());
+            assertTrue(testListener.isCompleted(5000));
 
 
             // SET CTD TO CANCEL IN FUTURE
@@ -204,6 +217,8 @@ public class TestUserApiError extends TestApiBase {
                     assertFalse(true);
                 }
             }
+            
+            assertListenerStatus();
         } catch (Exception e) {
             e.printStackTrace();
             Assert.assertFalse(true);
@@ -213,10 +228,12 @@ public class TestUserApiError extends TestApiBase {
 
     @Test(enabled=false, groups={"fast"})
     public void testCancelBadState() {
+        log.info("Starting testCancelBadState");
     }
 
     @Test(enabled=true, groups={"fast"})
     public void testUncancelBadState() {
+        log.info("Starting testUncancelBadState");
         try {
             Subscription subscription = createSubscription("Shotgun", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME);
 
@@ -230,7 +247,7 @@ public class TestUserApiError extends TestApiBase {
                     assertFalse(true);
                 }
             }
-
+            assertListenerStatus();
         } catch (Exception e) {
             e.printStackTrace();
             Assert.assertFalse(true);
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiRecreate.java b/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiRecreate.java
index b7506dd..389ac5f 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiRecreate.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiRecreate.java
@@ -25,9 +25,9 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.testng.Assert;
 
+import com.ning.billing.api.TestApiListener.NextEvent;
 import com.ning.billing.catalog.api.BillingPeriod;
 import com.ning.billing.catalog.api.PriceListSet;
-import com.ning.billing.entitlement.api.ApiTestListener.NextEvent;
 import com.ning.billing.entitlement.api.TestApiBase;
 
 public abstract class TestUserApiRecreate extends TestApiBase {
@@ -36,9 +36,10 @@ public abstract class TestUserApiRecreate extends TestApiBase {
 
 
     protected void testRecreateWithBPCanceledThroughSubscription() {
-        log.info("Starting testRecreateWithBPCanceled");
+        log.info("Starting testRecreateWithBPCanceledThroughSubscription");
         try {
             testCreateAndRecreate(false);
+            assertListenerStatus();
         } catch (EntitlementUserApiException e) {
             log.error("Unexpected exception",e);
             Assert.fail(e.getMessage());
@@ -46,9 +47,10 @@ public abstract class TestUserApiRecreate extends TestApiBase {
     }
 
     protected void testCreateWithBPCanceledFromUserApi() {
-        log.info("Starting testCreateWithBPCanceled");
+        log.info("Starting testCreateWithBPCanceledFromUserApi");
         try {
             testCreateAndRecreate(true);
+            assertListenerStatus();
         } catch (EntitlementUserApiException e) {
             log.error("Unexpected exception",e);
             Assert.fail(e.getMessage());
@@ -65,8 +67,8 @@ public abstract class TestUserApiRecreate extends TestApiBase {
         BillingPeriod term = BillingPeriod.MONTHLY;
         String planSetName = PriceListSet.DEFAULT_PRICELIST_NAME;
 
-        testListener.pushNextApiExpectedEvent(NextEvent.PHASE);
-        testListener.pushNextApiExpectedEvent(NextEvent.CREATE);
+        testListener.pushExpectedEvent(NextEvent.PHASE);
+        testListener.pushExpectedEvent(NextEvent.CREATE);
         SubscriptionData subscription = (SubscriptionData) entitlementApi.createSubscription(bundle.getId(),
                 getProductSpecifier(productName, planSetName, term, null), requestedDate, context);
         assertNotNull(subscription);
@@ -75,7 +77,7 @@ public abstract class TestUserApiRecreate extends TestApiBase {
         assertEquals(subscription.getStartDate(), requestedDate);
         assertEquals(productName, subscription.getCurrentPlan().getProduct().getName());
 
-        assertTrue(testListener.isApiCompleted(5000));
+        assertTrue(testListener.isCompleted(5000));
 
         // CREATE (AGAIN) WITH NEW PRODUCT
         productName = "Pistol";
@@ -95,11 +97,11 @@ public abstract class TestUserApiRecreate extends TestApiBase {
         }
 
         // NOW CANCEL ADN THIS SHOULD WORK
-        testListener.pushNextApiExpectedEvent(NextEvent.CANCEL);
+        testListener.pushExpectedEvent(NextEvent.CANCEL);
         subscription.cancel(null, false, context);
 
-        testListener.pushNextApiExpectedEvent(NextEvent.PHASE);
-        testListener.pushNextApiExpectedEvent(NextEvent.RE_CREATE);
+        testListener.pushExpectedEvent(NextEvent.PHASE);
+        testListener.pushExpectedEvent(NextEvent.RE_CREATE);
 
         // Avoid ordering issue for events at exact same date; this is actually a real good test, we
         // we test it at Beatrix level. At this level that would work for sql tests but not for in memory.
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiScenarios.java b/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiScenarios.java
index 92ac1b0..7f874de 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiScenarios.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiScenarios.java
@@ -21,17 +21,18 @@ import static org.testng.Assert.assertFalse;
 import static org.testng.Assert.assertTrue;
 
 import org.joda.time.DateTime;
+import org.joda.time.Interval;
 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.Duration;
 import com.ning.billing.catalog.api.PhaseType;
 import com.ning.billing.catalog.api.PlanPhase;
-import com.ning.billing.entitlement.api.ApiTestListener.NextEvent;
 import com.ning.billing.entitlement.api.TestApiBase;
 import com.ning.billing.entitlement.api.billing.EntitlementBillingApiException;
 import com.ning.billing.entitlement.glue.MockEngineModuleSql;
@@ -44,7 +45,7 @@ public class TestUserApiScenarios extends TestApiBase {
         return Guice.createInjector(Stage.DEVELOPMENT, new MockEngineModuleSql());
     }
 
-    @Test(enabled=true)
+    @Test(groups={"slow"}, enabled=true)
     public void testChangeIMMCancelUncancelChangeEOT() throws EntitlementBillingApiException {
 
         log.info("Starting testChangeIMMCancelUncancelChangeEOT");
@@ -54,14 +55,15 @@ public class TestUserApiScenarios extends TestApiBase {
             PlanPhase trialPhase = subscription.getCurrentPhase();
             assertEquals(trialPhase.getPhaseType(), PhaseType.TRIAL);
 
-            testListener.pushNextApiExpectedEvent(NextEvent.CHANGE);
+            testListener.pushExpectedEvent(NextEvent.CHANGE);
             subscription.changePlan("Pistol", BillingPeriod.ANNUAL, "gunclubDiscount", clock.getUTCNow(), context);
-            testListener.isApiCompleted(3000);
+            testListener.isCompleted(5000);
 
             // MOVE TO NEXT PHASE
-            testListener.pushNextApiExpectedEvent(NextEvent.PHASE);
-            clock.setDeltaFromReality(trialPhase.getDuration(), DAY_IN_MS);
-            assertTrue(testListener.isApiCompleted(2000));
+            testListener.pushExpectedEvent(NextEvent.PHASE);
+            Interval it = new Interval(clock.getUTCNow(), clock.getUTCNow().plusDays(31));
+            clock.addDeltaFromReality(it.toDurationMillis());
+            assertTrue(testListener.isCompleted(5000));
 
             // SET CTD
             Duration ctd = getDurationMonth(1);
@@ -71,22 +73,28 @@ public class TestUserApiScenarios extends TestApiBase {
             subscription = (SubscriptionData) entitlementApi.getSubscriptionFromId(subscription.getId());
 
             // CANCEL EOT
-            testListener.pushNextApiExpectedEvent(NextEvent.CANCEL);
+            testListener.setNonExpectedMode();
+            testListener.pushExpectedEvent(NextEvent.CANCEL);
             subscription.cancel(clock.getUTCNow(), false, context);
-            assertFalse(testListener.isApiCompleted(2000));
+            assertFalse(testListener.isCompleted(5000));
             testListener.reset();
 
             // UNCANCEL
             subscription.uncancel(context);
 
             // CHANGE EOT
-            testListener.pushNextApiExpectedEvent(NextEvent.CHANGE);
+            testListener.setNonExpectedMode();            
+            testListener.pushExpectedEvent(NextEvent.CHANGE);
             subscription.changePlan("Pistol", BillingPeriod.MONTHLY, "gunclubDiscount", clock.getUTCNow(), context);
-            assertFalse(testListener.isApiCompleted(2000));
-
-            clock.addDeltaFromReality(ctd);
-            assertTrue(testListener.isApiCompleted(3000));
+            assertFalse(testListener.isCompleted(5000));
+            testListener.reset();
+            
+            testListener.pushExpectedEvent(NextEvent.CHANGE);
+            it = new Interval(clock.getUTCNow(), clock.getUTCNow().plusMonths(1));
+            clock.addDeltaFromReality(it.toDurationMillis());
+            assertTrue(testListener.isCompleted(5000));
 
+            assertListenerStatus();
 
         } catch (EntitlementUserApiException e) {
             Assert.fail(e.getMessage());
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserCustomFieldsSql.java b/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserCustomFieldsSql.java
index 90966d8..c208521 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserCustomFieldsSql.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserCustomFieldsSql.java
@@ -28,9 +28,9 @@ 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.PriceListSet;
-import com.ning.billing.entitlement.api.ApiTestListener.NextEvent;
 import com.ning.billing.entitlement.api.TestApiBase;
 import com.ning.billing.entitlement.glue.MockEngineModuleSql;
 import com.ning.billing.util.callcontext.CallContext;
@@ -66,7 +66,7 @@ public class TestUserCustomFieldsSql extends TestApiBase {
 
     @Test(enabled=true, groups={"slow"})
     public void testOverwriteCustomFields() {
-        log.info("Starting testCreateWithRequestedDate");
+        log.info("Starting testOverwriteCustomFields");
         try {
 
             DateTime init = clock.getUTCNow();
@@ -76,8 +76,8 @@ public class TestUserCustomFieldsSql extends TestApiBase {
             BillingPeriod term = BillingPeriod.MONTHLY;
             String planSetName = PriceListSet.DEFAULT_PRICELIST_NAME;
 
-            testListener.pushNextApiExpectedEvent(NextEvent.PHASE);
-            testListener.pushNextApiExpectedEvent(NextEvent.CREATE);
+            testListener.pushExpectedEvent(NextEvent.PHASE);
+            testListener.pushExpectedEvent(NextEvent.CREATE);
             SubscriptionData subscription = (SubscriptionData) entitlementApi.createSubscription(bundle.getId(),
                     getProductSpecifier(productName, planSetName, term, null), requestedDate, context);
             assertNotNull(subscription);
@@ -122,7 +122,7 @@ public class TestUserCustomFieldsSql extends TestApiBase {
 
     @Test(enabled=true, groups={"slow"})
     public void testBasicCustomFields() {
-        log.info("Starting testCreateWithRequestedDate");
+        log.info("Starting testBasicCustomFields");
         try {
 
             DateTime init = clock.getUTCNow();
@@ -132,8 +132,8 @@ public class TestUserCustomFieldsSql extends TestApiBase {
             BillingPeriod term = BillingPeriod.MONTHLY;
             String planSetName = PriceListSet.DEFAULT_PRICELIST_NAME;
 
-            testListener.pushNextApiExpectedEvent(NextEvent.PHASE);
-            testListener.pushNextApiExpectedEvent(NextEvent.CREATE);
+            testListener.pushExpectedEvent(NextEvent.PHASE);
+            testListener.pushExpectedEvent(NextEvent.CREATE);
             SubscriptionData subscription = (SubscriptionData) entitlementApi.createSubscription(bundle.getId(),
                     getProductSpecifier(productName, planSetName, term, null), requestedDate, context);
             assertNotNull(subscription);
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 9b75099..5eb760c 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
@@ -158,7 +158,6 @@ public class MockEntitlementDaoMemory implements EntitlementDao, MockEntitlement
     @Override
     public void createSubscription(final SubscriptionData subscription, final List<EntitlementEvent> initialEvents,
                                    final CallContext context) {
-
         synchronized(events) {
             events.addAll(initialEvents);
             for (final EntitlementEvent cur : initialEvents) {
@@ -285,7 +284,7 @@ public class MockEntitlementDaoMemory implements EntitlementDao, MockEntitlement
     @Override
     public void cancelSubscription(final UUID subscriptionId, final EntitlementEvent cancelEvent,
                                    final CallContext context, final int seqId) {
-        synchronized (cancelEvent) {
+        synchronized(events) {
             cancelNextPhaseEvent(subscriptionId);
             insertEvent(cancelEvent);
         }
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/glue/MockEngineModule.java b/entitlement/src/test/java/com/ning/billing/entitlement/glue/MockEngineModule.java
index c795ca5..31e0da3 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/glue/MockEngineModule.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/glue/MockEngineModule.java
@@ -23,13 +23,12 @@ import com.ning.billing.util.clock.MockClockModule;
 import com.ning.billing.util.glue.BusModule;
 import com.ning.billing.util.glue.CallContextModule;
 
-public class MockEngineModule extends EntitlementModule {
+public class MockEngineModule extends DefaultEntitlementModule {
 
    
     @Override
     protected void configure() {
         super.configure();
-        install(new BusModule());
         install(new CatalogModule());
         bind(AccountUserApi.class).toInstance(BrainDeadProxyFactory.createBrainDeadProxyFor(AccountUserApi.class));
         install(new MockClockModule());
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/glue/MockEngineModuleMemory.java b/entitlement/src/test/java/com/ning/billing/entitlement/glue/MockEngineModuleMemory.java
index e46e4bc..148b62d 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/glue/MockEngineModuleMemory.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/glue/MockEngineModuleMemory.java
@@ -22,6 +22,8 @@ import com.ning.billing.entitlement.api.timeline.RepairEntitlementLifecycleDao;
 import com.ning.billing.entitlement.engine.dao.EntitlementDao;
 import com.ning.billing.entitlement.engine.dao.MockEntitlementDaoMemory;
 import com.ning.billing.entitlement.engine.dao.RepairEntitlementDao;
+import com.ning.billing.util.glue.BusModule;
+import com.ning.billing.util.glue.BusModule.BusType;
 import com.ning.billing.util.notificationq.MockNotificationQueueService;
 import com.ning.billing.util.notificationq.NotificationQueueService;
 
@@ -42,6 +44,7 @@ public class MockEngineModuleMemory extends MockEngineModule {
     @Override
     protected void configure() {
         super.configure();
+        install(new BusModule(BusType.MEMORY));
         installNotificationQueue();
     }
 }
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/glue/MockEngineModuleSql.java b/entitlement/src/test/java/com/ning/billing/entitlement/glue/MockEngineModuleSql.java
index 9195c91..e1b3742 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/glue/MockEngineModuleSql.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/glue/MockEngineModuleSql.java
@@ -32,8 +32,10 @@ import com.ning.billing.entitlement.engine.dao.MockEntitlementDaoSql;
 
 import com.ning.billing.entitlement.engine.dao.RepairEntitlementDao;
 
+import com.ning.billing.util.glue.BusModule;
 import com.ning.billing.util.glue.FieldStoreModule;
 import com.ning.billing.util.glue.NotificationQueueModule;
+import com.ning.billing.util.glue.BusModule.BusType;
 
 public class MockEngineModuleSql extends MockEngineModule {
 
@@ -65,6 +67,7 @@ public class MockEngineModuleSql extends MockEngineModule {
         installDBI();
         install(new NotificationQueueModule());
         install(new FieldStoreModule());
+        install(new BusModule(BusType.PERSISTENT));
         super.configure();
     }
 }

invoice/pom.xml 45(+12 -33)

diff --git a/invoice/pom.xml b/invoice/pom.xml
index 1762bc0..7d1911b 100644
--- a/invoice/pom.xml
+++ b/invoice/pom.xml
@@ -33,26 +33,25 @@
             <groupId>com.ning.billing</groupId>
             <artifactId>killbill-util</artifactId>
         </dependency>
-        <dependency>
-            <groupId>com.ning.billing</groupId>
-            <artifactId>killbill-util</artifactId>
-            <type>test-jar</type>
-            <scope>test</scope>
+         <dependency>
+            <groupId>org.jdbi</groupId>
+            <artifactId>jdbi</artifactId>
         </dependency>
         <dependency>
-            <groupId>com.ning.billing</groupId>
-            <artifactId>killbill-entitlement</artifactId>
-            <scope>test</scope>
+            <groupId>org.antlr</groupId>
+            <artifactId>stringtemplate</artifactId>
+            <scope>runtime</scope>
         </dependency>
         <dependency>
-            <groupId>com.ning.billing</groupId>
-            <artifactId>killbill-entitlement</artifactId>
-            <type>test-jar</type>
-            <scope>test</scope>
+            <groupId>com.google.inject</groupId>
+            <artifactId>guice</artifactId>
+            <scope>provided</scope>
         </dependency>
+       <!-- TEST SCOPE -->
         <dependency>
             <groupId>com.ning.billing</groupId>
-            <artifactId>killbill-junction</artifactId>
+            <artifactId>killbill-util</artifactId>
+            <type>test-jar</type>
             <scope>test</scope>
         </dependency>
         <dependency>
@@ -67,12 +66,6 @@
             <scope>test</scope>
         </dependency>
         <dependency>
-            <groupId>com.ning.billing</groupId>
-            <artifactId>killbill-account</artifactId>
-            <type>test-jar</type>
-            <scope>test</scope>
-        </dependency>
-        <dependency>
             <groupId>org.testng</groupId>
             <artifactId>testng</artifactId>
             <scope>test</scope>
@@ -83,20 +76,6 @@
             <scope>test</scope>
         </dependency>
         <dependency>
-            <groupId>org.jdbi</groupId>
-            <artifactId>jdbi</artifactId>
-        </dependency>
-        <dependency>
-            <groupId>org.antlr</groupId>
-            <artifactId>stringtemplate</artifactId>
-            <scope>runtime</scope>
-        </dependency>
-        <dependency>
-            <groupId>com.google.inject</groupId>
-            <artifactId>guice</artifactId>
-            <scope>provided</scope>
-        </dependency>
-        <dependency>
             <groupId>com.jayway.awaitility</groupId>
             <artifactId>awaitility</artifactId>
             <scope>test</scope>
diff --git a/invoice/src/main/java/com/ning/billing/invoice/dao/DefaultInvoiceDao.java b/invoice/src/main/java/com/ning/billing/invoice/dao/DefaultInvoiceDao.java
index 31294c0..5178a29 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/dao/DefaultInvoiceDao.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/dao/DefaultInvoiceDao.java
@@ -18,63 +18,46 @@ package com.ning.billing.invoice.dao;
 
 import java.math.BigDecimal;
 import java.util.ArrayList;
-import java.util.Collection;
-import java.util.HashMap;
 import java.util.List;
-import java.util.Map;
 import java.util.UUID;
 
-import com.ning.billing.util.dao.EntityAudit;
-import com.ning.billing.util.dao.ObjectType;
-import com.ning.billing.util.dao.TableName;
 import org.joda.time.DateTime;
 import org.skife.jdbi.v2.IDBI;
 import org.skife.jdbi.v2.Transaction;
 import org.skife.jdbi.v2.TransactionStatus;
 import org.skife.jdbi.v2.sqlobject.mixins.Transmogrifier;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
 
 import com.google.inject.Inject;
 import com.ning.billing.invoice.api.Invoice;
-import com.ning.billing.invoice.api.InvoiceCreationEvent;
 import com.ning.billing.invoice.api.InvoiceItem;
 import com.ning.billing.invoice.api.InvoicePayment;
-import com.ning.billing.invoice.api.user.DefaultInvoiceCreationEvent;
 import com.ning.billing.invoice.model.FixedPriceInvoiceItem;
 import com.ning.billing.invoice.model.RecurringInvoiceItem;
 import com.ning.billing.invoice.notification.NextBillingDatePoster;
-import com.ning.billing.junction.api.BillingApi;
 import com.ning.billing.util.ChangeType;
-import com.ning.billing.util.bus.Bus;
 import com.ning.billing.util.callcontext.CallContext;
 import com.ning.billing.util.customfield.CustomField;
 import com.ning.billing.util.customfield.dao.CustomFieldSqlDao;
+import com.ning.billing.util.dao.EntityAudit;
+import com.ning.billing.util.dao.ObjectType;
+import com.ning.billing.util.dao.TableName;
 import com.ning.billing.util.tag.ControlTagType;
 import com.ning.billing.util.tag.Tag;
 import com.ning.billing.util.tag.dao.TagDao;
 
 public class DefaultInvoiceDao implements InvoiceDao {
-    private final static Logger log = LoggerFactory.getLogger(DefaultInvoiceDao.class);
-
     private final InvoiceSqlDao invoiceSqlDao;
     private final InvoicePaymentSqlDao invoicePaymentSqlDao;
-    private final BillingApi billingApi;
     private final TagDao tagDao;
 
-    private final Bus eventBus;
-
 	private final NextBillingDatePoster nextBillingDatePoster;
 
     @Inject
-    public DefaultInvoiceDao(final IDBI dbi, final Bus eventBus,
-                             final BillingApi entitlementBillingApi,
+    public DefaultInvoiceDao(final IDBI dbi,
                              final NextBillingDatePoster nextBillingDatePoster,
                              final TagDao tagDao) {
         this.invoiceSqlDao = dbi.onDemand(InvoiceSqlDao.class);
         this.invoicePaymentSqlDao = dbi.onDemand(InvoicePaymentSqlDao.class);
-        this.eventBus = eventBus;
-        this.billingApi = entitlementBillingApi;
         this.nextBillingDatePoster = nextBillingDatePoster;
         this.tagDao = tagDao;
     }
@@ -140,7 +123,7 @@ public class DefaultInvoiceDao implements InvoiceDao {
         return invoiceSqlDao.inTransaction(new Transaction<Invoice, InvoiceSqlDao>() {
              @Override
              public Invoice inTransaction(final InvoiceSqlDao invoiceDao, final TransactionStatus status) throws Exception {
-                 Invoice invoice = invoiceDao.getById(invoiceId);
+                 Invoice invoice = invoiceDao.getById(invoiceId.toString());
 
                  if (invoice != null) {
                      populateChildren(invoice, invoiceDao);
@@ -153,62 +136,58 @@ public class DefaultInvoiceDao implements InvoiceDao {
 
     @Override
     public void create(final Invoice invoice, final CallContext context) {
+        
         invoiceSqlDao.inTransaction(new Transaction<Void, InvoiceSqlDao>() {
             @Override
-            public Void inTransaction(final InvoiceSqlDao invoiceDao, final TransactionStatus status) throws Exception {
+            public Void inTransaction(final InvoiceSqlDao transactional, final TransactionStatus status) throws Exception {
 
                 // STEPH this seems useless
-                Invoice currentInvoice = invoiceDao.getById(invoice.getId());
+                Invoice currentInvoice = transactional.getById(invoice.getId().toString());
 
                 if (currentInvoice == null) {
-                    invoiceDao.create(invoice, context);
+                    List<EntityAudit> audits = new ArrayList<EntityAudit>();
+
+                    transactional.create(invoice, context);
+                    Long recordId = transactional.getRecordId(invoice.getId().toString());
+                    audits.add(new EntityAudit(TableName.INVOICES, recordId, ChangeType.INSERT));
+
+                    List<Long> recordIdList;
 
                     List<InvoiceItem> recurringInvoiceItems = invoice.getInvoiceItems(RecurringInvoiceItem.class);
-                    RecurringInvoiceItemSqlDao recurringInvoiceItemDao = invoiceDao.become(RecurringInvoiceItemSqlDao.class);
+                    RecurringInvoiceItemSqlDao recurringInvoiceItemDao = transactional.become(RecurringInvoiceItemSqlDao.class);
                     recurringInvoiceItemDao.batchCreateFromTransaction(recurringInvoiceItems, context);
+                    recordIdList = recurringInvoiceItemDao.getRecordIds(invoice.getId().toString());
+                    audits.addAll(createAudits(TableName.RECURRING_INVOICE_ITEMS, recordIdList));
 
-                    notifyOfFutureBillingEvents(invoiceDao, recurringInvoiceItems);
+                    notifyOfFutureBillingEvents(transactional, recurringInvoiceItems);
 
                     List<InvoiceItem> fixedPriceInvoiceItems = invoice.getInvoiceItems(FixedPriceInvoiceItem.class);
-                    FixedPriceInvoiceItemSqlDao fixedPriceInvoiceItemDao = invoiceDao.become(FixedPriceInvoiceItemSqlDao.class);
+                    FixedPriceInvoiceItemSqlDao fixedPriceInvoiceItemDao = transactional.become(FixedPriceInvoiceItemSqlDao.class);
                     fixedPriceInvoiceItemDao.batchCreateFromTransaction(fixedPriceInvoiceItems, context);
+                    recordIdList = fixedPriceInvoiceItemDao.getRecordIds(invoice.getId().toString());
+                    audits.addAll(createAudits(TableName.FIXED_INVOICE_ITEMS, recordIdList));
 
-                    setChargedThroughDates(invoiceDao, fixedPriceInvoiceItems, recurringInvoiceItems, context);
-
-                    // STEPH Why do we need that? Are the payments not always null at this point?
                     List<InvoicePayment> invoicePayments = invoice.getPayments();
-                    InvoicePaymentSqlDao invoicePaymentSqlDao = invoiceDao.become(InvoicePaymentSqlDao.class);
-
-// TODO: add auditing and move to collections
-//                    Long maxRecordId = invoicePaymentSqlDao.getMaxRecordId(TableName.INVOICE_PAYMENTS);
-//                    invoicePaymentSqlDao.batchCreateFromTransaction(invoicePayments, context);
-//                    Map<UUID, Long> recordIdMap = invoicePaymentSqlDao.getRecordIdMap(maxRecordId);
-//
-//
-//
-//
-//                    auditSqlDao.insertAuditFromTransaction("invoices", invoice.getId().toString(), ChangeType.INSERT, context);
-//                    auditSqlDao.insertAuditFromTransaction("recurring_invoice_items", getIdsFromInvoiceItems(recurringInvoiceItems), ChangeType.INSERT, context);
-//                    auditSqlDao.insertAuditFromTransaction("fixed_invoice_items", getIdsFromInvoiceItems(fixedPriceInvoiceItems), ChangeType.INSERT, context);
-//                    auditSqlDao.insertAuditFromTransaction("invoice_payments", getIdsFromInvoicePayments(invoicePayments), ChangeType.INSERT, context);
+                    InvoicePaymentSqlDao invoicePaymentSqlDao = transactional.become(InvoicePaymentSqlDao.class);
+                    invoicePaymentSqlDao.batchCreateFromTransaction(invoicePayments, context);
+                    recordIdList = invoicePaymentSqlDao.getRecordIds(invoice.getId().toString());
+                    audits.addAll(createAudits(TableName.INVOICE_PAYMENTS, recordIdList));
 
+                    transactional.insertAuditFromTransaction(audits, context);
                 }
 
                 return null;
             }
         });
+    }
 
-        // TODO: move this inside the transaction once the bus is persistent
-        InvoiceCreationEvent event;
-        event = new DefaultInvoiceCreationEvent(invoice.getId(), invoice.getAccountId(),
-                                                      invoice.getBalance(), invoice.getCurrency(),
-                                                      invoice.getInvoiceDate(),
-                                                      context.getUserToken());
-        try {
-            eventBus.post(event);
-        } catch (Bus.EventBusException e) {
-            throw new RuntimeException(e);
+    private List<EntityAudit> createAudits(TableName tableName, List<Long> recordIdList) {
+        List<EntityAudit> entityAuditList = new ArrayList<EntityAudit>();
+        for (Long recordId : recordIdList) {
+            entityAuditList.add(new EntityAudit(tableName, recordId, ChangeType.INSERT));
         }
+
+        return entityAuditList;
     }
 
     @Override
@@ -238,9 +217,9 @@ public class DefaultInvoiceDao implements InvoiceDao {
                 transactional.notifyOfPaymentAttempt(invoicePayment, context);
 
                 String invoicePaymentId = invoicePayment.getId().toString();
-                Long recordId = transactional.getRecordId(TableName.INVOICE_PAYMENTS, invoicePaymentId);
-                EntityAudit audit = new EntityAudit(recordId, ChangeType.INSERT);
-                transactional.insertAuditFromTransaction(TableName.INVOICE_PAYMENTS, audit, context);
+                Long recordId = transactional.getRecordId(invoicePaymentId);
+                EntityAudit audit = new EntityAudit(TableName.INVOICE_PAYMENTS, recordId, ChangeType.INSERT);
+                transactional.insertAuditFromTransaction(audit, context);
 
                 return null;
             }
@@ -365,34 +344,4 @@ public class DefaultInvoiceDao implements InvoiceDao {
             }
         }
     }
-    
-    private void setChargedThroughDates(final InvoiceSqlDao dao, final Collection<InvoiceItem> fixedPriceItems,
-                                        final Collection<InvoiceItem> recurringItems, CallContext context) {
-        Map<UUID, DateTime> chargeThroughDates = new HashMap<UUID, DateTime>();
-        addInvoiceItemsToChargeThroughDates(chargeThroughDates, fixedPriceItems);
-        addInvoiceItemsToChargeThroughDates(chargeThroughDates, recurringItems);
-
-        for (UUID subscriptionId : chargeThroughDates.keySet()) {
-            if(subscriptionId != null) {
-                DateTime chargeThroughDate = chargeThroughDates.get(subscriptionId);
-                log.info("Setting CTD for subscription {} to {}", subscriptionId.toString(), chargeThroughDate.toString());
-                billingApi.setChargedThroughDateFromTransaction(dao, subscriptionId, chargeThroughDate, context);
-            }
-        }
-    }
-
-    private void addInvoiceItemsToChargeThroughDates(Map<UUID, DateTime> chargeThroughDates, Collection<InvoiceItem> items) {
-        for (InvoiceItem item : items) {
-            UUID subscriptionId = item.getSubscriptionId();
-            DateTime endDate = item.getEndDate();
-
-            if (chargeThroughDates.containsKey(subscriptionId)) {
-                if (chargeThroughDates.get(subscriptionId).isBefore(endDate)) {
-                    chargeThroughDates.put(subscriptionId, endDate);
-                }
-            } else {
-                chargeThroughDates.put(subscriptionId, endDate);
-            }
-        }
-    }
 }
diff --git a/invoice/src/main/java/com/ning/billing/invoice/dao/FixedPriceInvoiceItemSqlDao.java b/invoice/src/main/java/com/ning/billing/invoice/dao/FixedPriceInvoiceItemSqlDao.java
index 175cab6..d4fa5ee 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/dao/FixedPriceInvoiceItemSqlDao.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/dao/FixedPriceInvoiceItemSqlDao.java
@@ -27,6 +27,7 @@ import java.sql.SQLException;
 import java.util.List;
 import java.util.UUID;
 
+import com.ning.billing.util.entity.dao.EntitySqlDao;
 import org.joda.time.DateTime;
 import org.skife.jdbi.v2.SQLStatement;
 import org.skife.jdbi.v2.StatementContext;
@@ -46,11 +47,13 @@ import com.ning.billing.invoice.api.InvoiceItem;
 import com.ning.billing.invoice.model.FixedPriceInvoiceItem;
 import com.ning.billing.util.callcontext.CallContext;
 import com.ning.billing.util.callcontext.CallContextBinder;
-import com.ning.billing.util.entity.dao.EntityDao;
 
 @ExternalizedSqlViaStringTemplate3()
 @RegisterMapper(FixedPriceInvoiceItemSqlDao.FixedPriceInvoiceItemMapper.class)
-public interface FixedPriceInvoiceItemSqlDao extends EntityDao<InvoiceItem> {
+public interface FixedPriceInvoiceItemSqlDao extends EntitySqlDao<InvoiceItem> {
+    @SqlQuery
+    List<Long> getRecordIds(@Bind("invoiceId") final String invoiceId);
+
     @SqlQuery
     List<InvoiceItem> getInvoiceItemsByInvoice(@Bind("invoiceId") final String invoiceId);
 
diff --git a/invoice/src/main/java/com/ning/billing/invoice/dao/InvoicePaymentSqlDao.java b/invoice/src/main/java/com/ning/billing/invoice/dao/InvoicePaymentSqlDao.java
index 879f58d..a11c8e0 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/dao/InvoicePaymentSqlDao.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/dao/InvoicePaymentSqlDao.java
@@ -32,6 +32,7 @@ import com.ning.billing.util.dao.AuditSqlDao;
 import com.ning.billing.util.callcontext.CallContext;
 import com.ning.billing.util.callcontext.CallContextBinder;
 import com.ning.billing.util.dao.MapperBase;
+import com.ning.billing.util.entity.dao.EntitySqlDao;
 import org.joda.time.DateTime;
 import org.skife.jdbi.v2.SQLStatement;
 import org.skife.jdbi.v2.StatementContext;
@@ -52,7 +53,10 @@ import com.ning.billing.invoice.api.InvoicePayment;
 
 @ExternalizedSqlViaStringTemplate3
 @RegisterMapper(InvoicePaymentSqlDao.InvoicePaymentMapper.class)
-public interface InvoicePaymentSqlDao extends Transactional<InvoicePaymentSqlDao>, AuditSqlDao, Transmogrifier {
+public interface InvoicePaymentSqlDao extends EntitySqlDao<InvoicePayment>, Transactional<InvoicePaymentSqlDao>, AuditSqlDao, Transmogrifier {
+    @SqlQuery
+    List<Long> getRecordIds(@Bind("invoiceId") final String invoiceId);
+    
     @SqlQuery
     public InvoicePayment getByPaymentAttemptId(@Bind("paymentAttempt") final String paymentAttemptId);
 
@@ -82,9 +86,9 @@ public interface InvoicePaymentSqlDao extends Transactional<InvoicePaymentSqlDao
     public static class InvoicePaymentMapper extends MapperBase implements ResultSetMapper<InvoicePayment> {
         @Override
         public InvoicePayment map(int index, ResultSet result, StatementContext context) throws SQLException {
-            final UUID id = UUID.fromString(result.getString("id"));
-            final UUID paymentAttemptId = UUID.fromString(result.getString("payment_attempt_id"));
-            final UUID invoiceId = UUID.fromString(result.getString("invoice_id"));
+            final UUID id = getUUID(result, "id");
+            final UUID paymentAttemptId = getUUID(result, "payment_attempt_id");
+            final UUID invoiceId = getUUID(result, "invoice_id");
             final DateTime paymentAttemptDate = getDate(result, "payment_attempt_date");
             final BigDecimal amount = result.getBigDecimal("amount");
             final String currencyString = result.getString("currency");
diff --git a/invoice/src/main/java/com/ning/billing/invoice/dao/InvoiceSqlDao.java b/invoice/src/main/java/com/ning/billing/invoice/dao/InvoiceSqlDao.java
index 39e1016..af611bc 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/dao/InvoiceSqlDao.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/dao/InvoiceSqlDao.java
@@ -20,9 +20,10 @@ import com.ning.billing.catalog.api.Currency;
 import com.ning.billing.invoice.api.Invoice;
 import com.ning.billing.invoice.model.DefaultInvoice;
 import com.ning.billing.util.callcontext.CallContext;
+import com.ning.billing.util.dao.AuditSqlDao;
 import com.ning.billing.util.dao.UuidMapper;
 import com.ning.billing.util.callcontext.CallContextBinder;
-import com.ning.billing.util.entity.dao.EntityDao;
+import com.ning.billing.util.entity.dao.EntitySqlDao;
 import org.joda.time.DateTime;
 import org.skife.jdbi.v2.SQLStatement;
 import org.skife.jdbi.v2.StatementContext;
@@ -53,7 +54,7 @@ import java.util.UUID;
 
 @ExternalizedSqlViaStringTemplate3()
 @RegisterMapper(InvoiceSqlDao.InvoiceMapper.class)
-public interface InvoiceSqlDao extends EntityDao<Invoice>, Transactional<InvoiceSqlDao>, Transmogrifier, CloseMe {
+public interface InvoiceSqlDao extends EntitySqlDao<Invoice>, AuditSqlDao, Transactional<InvoiceSqlDao>, Transmogrifier, CloseMe {
     @Override
     @SqlUpdate
     void create(@InvoiceBinder Invoice invoice, @CallContextBinder final CallContext context);
diff --git a/invoice/src/main/java/com/ning/billing/invoice/dao/RecurringInvoiceItemSqlDao.java b/invoice/src/main/java/com/ning/billing/invoice/dao/RecurringInvoiceItemSqlDao.java
index 3fc76ee..4daa560 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/dao/RecurringInvoiceItemSqlDao.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/dao/RecurringInvoiceItemSqlDao.java
@@ -27,7 +27,7 @@ import java.sql.SQLException;
 import java.util.List;
 import java.util.UUID;
 
-import com.ning.billing.util.entity.dao.EntityDao;
+import com.ning.billing.util.dao.MapperBase;
 import org.joda.time.DateTime;
 import org.skife.jdbi.v2.SQLStatement;
 import org.skife.jdbi.v2.StatementContext;
@@ -47,10 +47,14 @@ import com.ning.billing.invoice.api.InvoiceItem;
 import com.ning.billing.invoice.model.RecurringInvoiceItem;
 import com.ning.billing.util.callcontext.CallContext;
 import com.ning.billing.util.callcontext.CallContextBinder;
+import com.ning.billing.util.entity.dao.EntitySqlDao;
 
 @ExternalizedSqlViaStringTemplate3()
 @RegisterMapper(RecurringInvoiceItemSqlDao.RecurringInvoiceItemMapper.class)
-public interface RecurringInvoiceItemSqlDao extends EntityDao<InvoiceItem> {
+public interface RecurringInvoiceItemSqlDao extends EntitySqlDao<InvoiceItem> {
+    @SqlQuery
+    List<Long> getRecordIds(@Bind("invoiceId") final String invoiceId);
+
     @SqlQuery
     List<InvoiceItem> getInvoiceItemsByInvoice(@Bind("invoiceId") final String invoiceId);
 
@@ -96,23 +100,22 @@ public interface RecurringInvoiceItemSqlDao extends EntityDao<InvoiceItem> {
         }
     }
 
-    public static class RecurringInvoiceItemMapper implements ResultSetMapper<InvoiceItem> {
+    public static class RecurringInvoiceItemMapper extends MapperBase implements ResultSetMapper<InvoiceItem> {
         @Override
         public InvoiceItem map(int index, ResultSet result, StatementContext context) throws SQLException {
-            UUID id = UUID.fromString(result.getString("id"));
-            UUID invoiceId = UUID.fromString(result.getString("invoice_id"));
-            UUID accountId = UUID.fromString(result.getString("account_id"));
-            UUID subscriptionId = result.getString("subscription_id") == null ? null : UUID.fromString(result.getString("subscription_id"));
-            UUID bundleId = result.getString("bundle_id") == null ? null : UUID.fromString(result.getString("bundle_id"));
+            UUID id = getUUID(result, "id");
+            UUID invoiceId = getUUID(result, "invoice_id");
+            UUID accountId = getUUID(result, "account_id");
+            UUID subscriptionId = getUUID(result, "subscription_id");
+            UUID bundleId = getUUID(result, "bundle_id");
             String planName = result.getString("plan_name");
             String phaseName = result.getString("phase_name");
-            DateTime startDate = new DateTime(result.getTimestamp("start_date"));
-            DateTime endDate = new DateTime(result.getTimestamp("end_date"));
+            DateTime startDate = getDate(result, "start_date");
+            DateTime endDate = getDate(result, "end_date");
             BigDecimal amount = result.getBigDecimal("amount");
             BigDecimal rate = result.getBigDecimal("rate");
             Currency currency = Currency.valueOf(result.getString("currency"));
-            String reversedItemString = result.getString("reversed_item_id");
-            UUID reversedItemId = (reversedItemString == null) ? null : UUID.fromString(reversedItemString);
+            UUID reversedItemId = getUUID(result, "reversed_item_id");
 
             return new RecurringInvoiceItem(id, invoiceId, accountId, bundleId, subscriptionId, planName, phaseName, startDate, endDate,
                     amount, rate, currency, reversedItemId);
diff --git a/invoice/src/main/java/com/ning/billing/invoice/InvoiceDispatcher.java b/invoice/src/main/java/com/ning/billing/invoice/InvoiceDispatcher.java
index 53093a2..32d444d 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/InvoiceDispatcher.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/InvoiceDispatcher.java
@@ -17,7 +17,9 @@
 package com.ning.billing.invoice;
 
 import java.util.Collection;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 import java.util.SortedSet;
 import java.util.UUID;
 
@@ -32,15 +34,20 @@ import com.ning.billing.account.api.AccountApiException;
 import com.ning.billing.account.api.AccountUserApi;
 import com.ning.billing.catalog.api.Currency;
 import com.ning.billing.entitlement.api.billing.BillingEvent;
+import com.ning.billing.entitlement.api.billing.EntitlementBillingApiException;
 import com.ning.billing.entitlement.api.user.SubscriptionEvent;
 import com.ning.billing.invoice.api.Invoice;
+import com.ning.billing.invoice.api.InvoiceCreationEvent;
 import com.ning.billing.invoice.api.InvoiceNotifier;
 import com.ning.billing.invoice.api.InvoiceApiException;
 import com.ning.billing.invoice.api.InvoiceItem;
 import com.ning.billing.invoice.api.user.DefaultEmptyInvoiceEvent;
+import com.ning.billing.invoice.api.user.DefaultInvoiceCreationEvent;
 import com.ning.billing.invoice.dao.InvoiceDao;
 import com.ning.billing.invoice.model.BillingEventSet;
+import com.ning.billing.invoice.model.FixedPriceInvoiceItem;
 import com.ning.billing.invoice.model.InvoiceGenerator;
+import com.ning.billing.invoice.model.RecurringInvoiceItem;
 import com.ning.billing.junction.api.BillingApi;
 import com.ning.billing.util.bus.Bus;
 import com.ning.billing.util.bus.Bus.EventBusException;
@@ -53,7 +60,7 @@ import com.ning.billing.util.globallocker.GlobalLocker.LockerService;
 import com.ning.billing.util.globallocker.LockFailedException;
 
 public class InvoiceDispatcher {
-	private final static Logger log = LoggerFactory.getLogger(InvoiceDispatcher.class);
+    private final static Logger log = LoggerFactory.getLogger(InvoiceDispatcher.class);
     private final static int NB_LOCK_TRY = 5;
 
     private final InvoiceGenerator generator;
@@ -69,12 +76,12 @@ public class InvoiceDispatcher {
 
     @Inject
     public InvoiceDispatcher(final InvoiceGenerator generator, final AccountUserApi accountUserApi,
-                             final BillingApi billingApi,
-                             final InvoiceDao invoiceDao,
-                             final InvoiceNotifier invoiceNotifier,
-                             final GlobalLocker locker,
-                             final Bus eventBus,
-                             final Clock clock) {
+            final BillingApi billingApi,
+            final InvoiceDao invoiceDao,
+            final InvoiceNotifier invoiceNotifier,
+            final GlobalLocker locker,
+            final Bus eventBus,
+            final Clock clock) {
         this.generator = generator;
         this.billingApi = billingApi;
         this.accountUserApi = accountUserApi;
@@ -89,7 +96,7 @@ public class InvoiceDispatcher {
     }
 
     public void processSubscription(final SubscriptionEvent transition,
-                                    final CallContext context) throws InvoiceApiException {
+            final CallContext context) throws InvoiceApiException {
         UUID subscriptionId = transition.getSubscriptionId();
         DateTime targetDate = transition.getEffectiveTransitionTime();
         log.info("Got subscription transition from InvoiceListener. id: " + subscriptionId.toString() + "; targetDate: " + targetDate.toString());
@@ -98,25 +105,24 @@ public class InvoiceDispatcher {
     }
 
     public void processSubscription(final UUID subscriptionId, final DateTime targetDate,
-                                    final CallContext context) throws InvoiceApiException {
-        if (subscriptionId == null) {
-            log.error("Failed handling entitlement change.", new InvoiceApiException(ErrorCode.INVOICE_INVALID_TRANSITION));
-            return;
-        }
-
-        UUID accountId = billingApi.getAccountIdFromSubscriptionId(subscriptionId);
-        if (accountId == null) {
+            final CallContext context) throws InvoiceApiException {
+        try {
+            if (subscriptionId == null) {
+                log.error("Failed handling entitlement change.", new InvoiceApiException(ErrorCode.INVOICE_INVALID_TRANSITION));
+                return;
+            }
+            UUID accountId = billingApi.getAccountIdFromSubscriptionId(subscriptionId);
+            processAccount(accountId, targetDate, false, context);
+        } catch (EntitlementBillingApiException e) {
             log.error("Failed handling entitlement change.",
                     new InvoiceApiException(ErrorCode.INVOICE_NO_ACCOUNT_ID_FOR_SUBSCRIPTION_ID, subscriptionId.toString()));
-            return;
         }
-
-        processAccount(accountId, targetDate, false, context);
+        return;
     }
-    
+
     public Invoice processAccount(final UUID accountId, final DateTime targetDate,
-                                  final boolean dryRun, final CallContext context) throws InvoiceApiException {
-		GlobalLock lock = null;
+            final boolean dryRun, final CallContext context) throws InvoiceApiException {
+        GlobalLock lock = null;
         try {
             lock = locker.lockWithNumberOfTries(LockerService.INVOICE, accountId.toString(), NB_LOCK_TRY);
 
@@ -134,15 +140,7 @@ public class InvoiceDispatcher {
         return null;
     }
 
-    private void postEmptyInvoiceEvent(final UUID accountId, final UUID userToken) {
-        try {
-            BusEvent event = new DefaultEmptyInvoiceEvent(accountId, clock.getUTCNow(), userToken);
-            eventBus.post(event);
-        } catch (EventBusException e){
-            log.error("Failed to post DefaultEmptyInvoiceNotification event for account {} ", accountId, e);
-        }
-    }
-
+   
     private Invoice processAccountWithLock(final UUID accountId, final DateTime targetDate,
             final boolean dryRun, final CallContext context) throws InvoiceApiException {
         try {
@@ -159,7 +157,8 @@ public class InvoiceDispatcher {
                 log.info("Generated null invoice.");
                 outputDebugData(events, invoices);
                 if (!dryRun) {
-                    postEmptyInvoiceEvent(accountId, context.getUserToken());
+                    BusEvent event = new DefaultEmptyInvoiceEvent(accountId, clock.getUTCNow(), context.getUserToken());
+                    postEvent(event, accountId);
                 }
             } else {
                 log.info("Generated invoice {} with {} items.", invoice.getId().toString(), invoice.getNumberOfItems());
@@ -172,6 +171,17 @@ public class InvoiceDispatcher {
                 outputDebugData(events, invoices);
                 if (!dryRun) {
                     invoiceDao.create(invoice, context);
+
+                    List<InvoiceItem> fixedPriceInvoiceItems = invoice.getInvoiceItems(FixedPriceInvoiceItem.class);
+                    List<InvoiceItem> recurringInvoiceItems = invoice.getInvoiceItems(RecurringInvoiceItem.class);
+                    setChargedThroughDates(fixedPriceInvoiceItems, recurringInvoiceItems, context);
+
+                    final InvoiceCreationEvent event = new DefaultInvoiceCreationEvent(invoice.getId(), invoice.getAccountId(),
+                            invoice.getBalance(), invoice.getCurrency(),
+                            invoice.getInvoiceDate(),
+                            context.getUserToken());
+
+                    postEvent(event, accountId);
                 }
             }
 
@@ -186,6 +196,47 @@ public class InvoiceDispatcher {
         }
     }
 
+    private void setChargedThroughDates(final Collection<InvoiceItem> fixedPriceItems,
+            final Collection<InvoiceItem> recurringItems, CallContext context) {
+
+        Map<UUID, DateTime> chargeThroughDates = new HashMap<UUID, DateTime>();
+        addInvoiceItemsToChargeThroughDates(chargeThroughDates, fixedPriceItems);
+        addInvoiceItemsToChargeThroughDates(chargeThroughDates, recurringItems);
+
+        for (UUID subscriptionId : chargeThroughDates.keySet()) {
+            if(subscriptionId != null) {
+                DateTime chargeThroughDate = chargeThroughDates.get(subscriptionId);
+                log.info("Setting CTD for subscription {} to {}", subscriptionId.toString(), chargeThroughDate.toString());
+                billingApi.setChargedThroughDate(subscriptionId, chargeThroughDate, context);
+            }
+        }
+    }
+    
+    private void postEvent(final BusEvent event, final UUID accountId) {
+        try {
+            eventBus.post(event);
+        } catch (EventBusException e){
+            log.error(String.format("Failed to post event {} for account {} ", event.getBusEventType(), accountId), e);
+        }
+    }
+
+
+    private void addInvoiceItemsToChargeThroughDates(Map<UUID, DateTime> chargeThroughDates, Collection<InvoiceItem> items) {
+        for (InvoiceItem item : items) {
+            UUID subscriptionId = item.getSubscriptionId();
+            DateTime endDate = item.getEndDate();
+
+            if (chargeThroughDates.containsKey(subscriptionId)) {
+                if (chargeThroughDates.get(subscriptionId).isBefore(endDate)) {
+                    chargeThroughDates.put(subscriptionId, endDate);
+                }
+            } else {
+                chargeThroughDates.put(subscriptionId, endDate);
+            }
+        }
+    }
+
+
     private void outputDebugData(Collection<BillingEvent> events, Collection<Invoice> invoices) {
         if (VERBOSE_OUTPUT) {
             log.info("Events");
diff --git a/invoice/src/main/resources/com/ning/billing/invoice/dao/FixedPriceInvoiceItemSqlDao.sql.stg b/invoice/src/main/resources/com/ning/billing/invoice/dao/FixedPriceInvoiceItemSqlDao.sql.stg
index 659d876..d7aed8c 100644
--- a/invoice/src/main/resources/com/ning/billing/invoice/dao/FixedPriceInvoiceItemSqlDao.sql.stg
+++ b/invoice/src/main/resources/com/ning/billing/invoice/dao/FixedPriceInvoiceItemSqlDao.sql.stg
@@ -53,6 +53,28 @@ batchCreateFromTransaction() ::= <<
          :startDate, :endDate, :amount, :currency, :userName, :createdDate);
 >>
 
+getRecordIds() ::= <<
+    SELECT record_id, id
+    FROM fixed_invoice_items
+    WHERE invoice_id = :invoiceId;
+>>
+
+auditFields(prefix) ::= <<
+    <prefix>table_name,
+    <prefix>record_id,
+    <prefix>change_type,
+    <prefix>change_date,
+    <prefix>changed_by,
+    <prefix>reason_code,
+    <prefix>comments,
+    <prefix>user_token
+>>
+
+insertAuditFromTransaction() ::= <<
+    INSERT INTO audit_log(<auditFields()>)
+    VALUES(:tableName, :recordId, :changeType, :createdDate, :userName, NULL, NULL, NULL);
+>>
+
 test() ::= <<
   SELECT 1
   FROM fixed_invoice_items;
diff --git a/invoice/src/main/resources/com/ning/billing/invoice/dao/InvoicePaymentSqlDao.sql.stg b/invoice/src/main/resources/com/ning/billing/invoice/dao/InvoicePaymentSqlDao.sql.stg
index a673b81..c6989bb 100644
--- a/invoice/src/main/resources/com/ning/billing/invoice/dao/InvoicePaymentSqlDao.sql.stg
+++ b/invoice/src/main/resources/com/ning/billing/invoice/dao/InvoicePaymentSqlDao.sql.stg
@@ -49,6 +49,28 @@ getInvoicePayment() ::= <<
     WHERE payment_id = :payment_id;
 >>
 
+getRecordIds() ::= <<
+    SELECT record_id, id
+    FROM invoice_payments
+    WHERE invoice_id = :invoiceId;
+>>
+
+auditFields(prefix) ::= <<
+    <prefix>table_name,
+    <prefix>record_id,
+    <prefix>change_type,
+    <prefix>change_date,
+    <prefix>changed_by,
+    <prefix>reason_code,
+    <prefix>comments,
+    <prefix>user_token
+>>
+
+insertAuditFromTransaction() ::= <<
+    INSERT INTO audit_log(<auditFields()>)
+    VALUES(:tableName, :recordId, :changeType, :createdDate, :userName, NULL, NULL, NULL);
+>>
+
 test() ::= <<
   SELECT 1 FROM invoice_payments;
 >>
diff --git a/invoice/src/main/resources/com/ning/billing/invoice/dao/InvoiceSqlDao.sql.stg b/invoice/src/main/resources/com/ning/billing/invoice/dao/InvoiceSqlDao.sql.stg
index b83dc82..f8efa27 100644
--- a/invoice/src/main/resources/com/ning/billing/invoice/dao/InvoiceSqlDao.sql.stg
+++ b/invoice/src/main/resources/com/ning/billing/invoice/dao/InvoiceSqlDao.sql.stg
@@ -1,18 +1,6 @@
 group InvoiceDao;
 
-invoiceFetchFields(prefix) ::= <<
-    <prefix>invoice_number,
-    <prefix>id,
-    <prefix>account_id,
-    <prefix>invoice_date,
-    <prefix>target_date,
-    <prefix>currency,
-  	<prefix>migrated,
-    <prefix>created_by,
-    <prefix>created_date
->>
-
-invoiceSetFields(prefix) ::= <<
+invoiceFields(prefix) ::= <<
     <prefix>id,
     <prefix>account_id,
     <prefix>invoice_date,
@@ -24,42 +12,42 @@ invoiceSetFields(prefix) ::= <<
 >>
 
 get() ::= <<
-  SELECT <invoiceFetchFields()>
+  SELECT record_id as invoice_number, <invoiceFields()>
   FROM invoices
   ORDER BY target_date ASC;
 >>
 
 getInvoicesByAccount() ::= <<
-  SELECT <invoiceFetchFields()>
+  SELECT record_id as invoice_number, <invoiceFields()>
   FROM invoices
   WHERE account_id = :accountId AND migrated = 'FALSE'
   ORDER BY target_date ASC;
 >>
 
 getAllInvoicesByAccount() ::= <<
-  SELECT <invoiceFetchFields()>
+  SELECT record_id as invoice_number, <invoiceFields()>
   FROM invoices
   WHERE account_id = :accountId
   ORDER BY target_date ASC;
 >>
 
 getInvoicesByAccountAfterDate() ::= <<
-  SELECT <invoiceFetchFields()>
+  SELECT record_id as invoice_number, <invoiceFields()>
   FROM invoices
   WHERE account_id = :accountId AND target_date >= :fromDate AND migrated = 'FALSE'
   ORDER BY target_date ASC;
 >>
 
 getInvoicesBySubscription() ::= <<
-  SELECT <invoiceFetchFields("i.")>
+  SELECT record_id as invoice_number, <invoiceFields("i.")>
   FROM invoices i
   LEFT JOIN recurring_invoice_items rii ON i.id = rii.invoice_id
   WHERE rii.subscription_id = :subscriptionId  AND migrated = 'FALSE'
-  GROUP BY <invoiceFetchFields("i.")>;
+  GROUP BY record_id as invoice_number, <invoiceFields("i.")>;
 >>
 
 getById() ::= <<
-  SELECT <invoiceFetchFields()>
+  SELECT record_id as invoice_number, <invoiceFields()>
   FROM invoices
   WHERE id = :id;
 >>
@@ -75,7 +63,7 @@ getAccountBalance() ::= <<
 >>
 
 create() ::= <<
-  INSERT INTO invoices(<invoiceSetFields()>)
+  INSERT INTO invoices(<invoiceFields()>)
   VALUES (:id, :accountId, :invoiceDate, :targetDate, :currency, :migrated, :userName, :createdDate);
 >>
 
@@ -87,7 +75,7 @@ getInvoiceIdByPaymentAttemptId() ::= <<
 >>
 
 getUnpaidInvoicesByAccountId() ::= <<
-  SELECT <invoiceFetchFields("i.")>
+  SELECT record_id as invoice_number, <invoiceFields("i.")>
   FROM invoices i
   LEFT JOIN invoice_payment_summary ips ON i.id = ips.invoice_id
   LEFT JOIN invoice_item_summary iis ON i.id = iis.invoice_id
@@ -97,6 +85,28 @@ getUnpaidInvoicesByAccountId() ::= <<
   ORDER BY i.target_date ASC;
 >>
 
+getRecordId() ::= <<
+    SELECT record_id
+    FROM invoices
+    WHERE id = :id;
+>>
+
+auditFields(prefix) ::= <<
+    <prefix>table_name,
+    <prefix>record_id,
+    <prefix>change_type,
+    <prefix>change_date,
+    <prefix>changed_by,
+    <prefix>reason_code,
+    <prefix>comments,
+    <prefix>user_token
+>>
+
+insertAuditFromTransaction() ::= <<
+    INSERT INTO audit_log(<auditFields()>)
+    VALUES(:tableName, :recordId, :changeType, :createdDate, :userName, NULL, NULL, NULL);
+>>
+
 test() ::= <<
   SELECT 1
   FROM invoices;
diff --git a/invoice/src/main/resources/com/ning/billing/invoice/dao/RecurringInvoiceItemSqlDao.sql.stg b/invoice/src/main/resources/com/ning/billing/invoice/dao/RecurringInvoiceItemSqlDao.sql.stg
index 7573ef3..8afb199 100644
--- a/invoice/src/main/resources/com/ning/billing/invoice/dao/RecurringInvoiceItemSqlDao.sql.stg
+++ b/invoice/src/main/resources/com/ning/billing/invoice/dao/RecurringInvoiceItemSqlDao.sql.stg
@@ -55,6 +55,28 @@ batchCreateFromTransaction() ::= <<
          :amount, :rate, :currency, :reversedItemId, :userName, :createdDate);
 >>
 
+getRecordIds() ::= <<
+    SELECT record_id, id
+    FROM recurring_invoice_items
+    WHERE invoice_id = :invoiceId;
+>>
+
+auditFields(prefix) ::= <<
+    <prefix>table_name,
+    <prefix>record_id,
+    <prefix>change_type,
+    <prefix>change_date,
+    <prefix>changed_by,
+    <prefix>reason_code,
+    <prefix>comments,
+    <prefix>user_token
+>>
+
+insertAuditFromTransaction() ::= <<
+    INSERT INTO audit_log(<auditFields()>)
+    VALUES(:tableName, :recordId, :changeType, :createdDate, :userName, NULL, NULL, NULL);
+>>
+
 test() ::= <<
   SELECT 1
   FROM recurring_invoice_items;
diff --git a/invoice/src/test/java/com/ning/billing/invoice/api/migration/MockModuleNoEntitlement.java b/invoice/src/test/java/com/ning/billing/invoice/api/migration/MockModuleNoEntitlement.java
index e2d52e3..fe5d886 100644
--- a/invoice/src/test/java/com/ning/billing/invoice/api/migration/MockModuleNoEntitlement.java
+++ b/invoice/src/test/java/com/ning/billing/invoice/api/migration/MockModuleNoEntitlement.java
@@ -16,16 +16,15 @@
 
 package com.ning.billing.invoice.api.migration;
 
-import com.ning.billing.entitlement.api.billing.ChargeThruApi;
 import com.ning.billing.invoice.MockModule;
 import com.ning.billing.invoice.api.InvoiceNotifier;
-import com.ning.billing.invoice.glue.InvoiceModule;
+import com.ning.billing.invoice.glue.DefaultInvoiceModule;
 import com.ning.billing.invoice.notification.NextBillingDateNotifier;
 import com.ning.billing.invoice.notification.NextBillingDatePoster;
 import com.ning.billing.invoice.notification.NullInvoiceNotifier;
-import com.ning.billing.junction.api.BillingApi;
 import com.ning.billing.mock.BrainDeadProxyFactory;
 import com.ning.billing.mock.BrainDeadProxyFactory.ZombieControl;
+import com.ning.billing.util.email.templates.TemplateModule;
 
 public class MockModuleNoEntitlement extends MockModule {
 //	@Override
@@ -42,7 +41,7 @@ public class MockModuleNoEntitlement extends MockModule {
 
 	@Override
 	protected void installInvoiceModule() {
-		install(new InvoiceModule(){
+		install(new DefaultInvoiceModule(){
 
 			@Override
 			protected void installNotifiers() {
@@ -55,7 +54,8 @@ public class MockModuleNoEntitlement extends MockModule {
 			
 			
 		});
-		
+        install(new TemplateModule());
+
 		
 	}
 
diff --git a/invoice/src/test/java/com/ning/billing/invoice/api/migration/TestDefaultInvoiceMigrationApi.java b/invoice/src/test/java/com/ning/billing/invoice/api/migration/TestDefaultInvoiceMigrationApi.java
index 1926c72..efedbaf 100644
--- a/invoice/src/test/java/com/ning/billing/invoice/api/migration/TestDefaultInvoiceMigrationApi.java
+++ b/invoice/src/test/java/com/ning/billing/invoice/api/migration/TestDefaultInvoiceMigrationApi.java
@@ -132,7 +132,7 @@ public class TestDefaultInvoiceMigrationApi extends InvoicingTestBase {
 
 		busService.getBus().start();
 
-        ((ZombieControl)billingApi).addResult("setChargedThroughDateFromTransaction", BrainDeadProxyFactory.ZOMBIE_VOID);
+        ((ZombieControl)billingApi).addResult("setChargedThroughDate", BrainDeadProxyFactory.ZOMBIE_VOID);
 		migrationInvoiceId = createAndCheckMigrationInvoice();
 		regularInvoiceId = generateRegularInvoice();
 
@@ -189,11 +189,10 @@ public class TestDefaultInvoiceMigrationApi extends InvoicingTestBase {
                 fixedPrice, BigDecimal.ONE, currency, BillingPeriod.MONTHLY, 1,
                 BillingModeType.IN_ADVANCE, "", 1L, SubscriptionTransitionType.CREATE));
 
-		BillingApi entitlementBillingApi = BrainDeadProxyFactory.createBrainDeadProxyFor(BillingApi.class);
-        ((ZombieControl)entitlementBillingApi).addResult("getBillingEventsForAccountAndUpdateAccountBCD", events);
+		((ZombieControl)billingApi).addResult("getBillingEventsForAccountAndUpdateAccountBCD", events);
 
         InvoiceNotifier invoiceNotifier = new NullInvoiceNotifier();
-		InvoiceDispatcher dispatcher = new InvoiceDispatcher(generator, accountUserApi, entitlementBillingApi,
+		InvoiceDispatcher dispatcher = new InvoiceDispatcher(generator, accountUserApi, billingApi,
                                                              invoiceDao, invoiceNotifier, locker, busService.getBus(), clock);
 
         CallContext context = new DefaultCallContextFactory(clock).createCallContext("Migration test", CallOrigin.TEST, UserType.TEST);
diff --git a/invoice/src/test/java/com/ning/billing/invoice/dao/InvoiceItemDaoTests.java b/invoice/src/test/java/com/ning/billing/invoice/dao/InvoiceItemDaoTests.java
index 6da2581..9acde8e 100644
--- a/invoice/src/test/java/com/ning/billing/invoice/dao/InvoiceItemDaoTests.java
+++ b/invoice/src/test/java/com/ning/billing/invoice/dao/InvoiceItemDaoTests.java
@@ -47,7 +47,7 @@ public class InvoiceItemDaoTests extends InvoiceDaoTestBase {
                 rate, rate, Currency.USD);
         recurringInvoiceItemDao.create(item, context);
 
-        RecurringInvoiceItem thisItem = (RecurringInvoiceItem) recurringInvoiceItemDao.getById(item.getId());
+        RecurringInvoiceItem thisItem = (RecurringInvoiceItem) recurringInvoiceItemDao.getById(item.getId().toString());
         assertNotNull(thisItem);
         assertEquals(thisItem.getId(), item.getId());
         assertEquals(thisItem.getInvoiceId(), item.getInvoiceId());
diff --git a/invoice/src/test/java/com/ning/billing/invoice/glue/InvoiceModuleWithEmbeddedDb.java b/invoice/src/test/java/com/ning/billing/invoice/glue/InvoiceModuleWithEmbeddedDb.java
index b35062d..081b21f 100644
--- a/invoice/src/test/java/com/ning/billing/invoice/glue/InvoiceModuleWithEmbeddedDb.java
+++ b/invoice/src/test/java/com/ning/billing/invoice/glue/InvoiceModuleWithEmbeddedDb.java
@@ -21,14 +21,12 @@ import static org.testng.Assert.assertNotNull;
 import java.io.IOException;
 import java.net.URL;
 
-import com.ning.billing.entitlement.api.user.EntitlementUserApi;
-import com.ning.billing.invoice.api.InvoiceNotifier;
-import com.ning.billing.invoice.notification.NullInvoiceNotifier;
 import org.skife.jdbi.v2.IDBI;
 
 import com.ning.billing.account.api.AccountUserApi;
 import com.ning.billing.catalog.glue.CatalogModule;
 import com.ning.billing.dbi.MysqlTestingHelper;
+import com.ning.billing.invoice.api.InvoiceNotifier;
 import com.ning.billing.invoice.api.test.DefaultInvoiceTestApi;
 import com.ning.billing.invoice.api.test.InvoiceTestApi;
 import com.ning.billing.invoice.dao.InvoicePaymentSqlDao;
@@ -37,13 +35,16 @@ import com.ning.billing.invoice.notification.MockNextBillingDateNotifier;
 import com.ning.billing.invoice.notification.MockNextBillingDatePoster;
 import com.ning.billing.invoice.notification.NextBillingDateNotifier;
 import com.ning.billing.invoice.notification.NextBillingDatePoster;
+import com.ning.billing.invoice.notification.NullInvoiceNotifier;
 import com.ning.billing.junction.api.BillingApi;
 import com.ning.billing.mock.BrainDeadProxyFactory;
 import com.ning.billing.mock.BrainDeadProxyFactory.ZombieControl;
+import com.ning.billing.mock.glue.MockEntitlementModule;
 import com.ning.billing.util.callcontext.CallContextFactory;
 import com.ning.billing.util.callcontext.DefaultCallContextFactory;
 import com.ning.billing.util.clock.Clock;
 import com.ning.billing.util.clock.DefaultClock;
+import com.ning.billing.util.email.templates.TemplateModule;
 import com.ning.billing.util.glue.BusModule;
 import com.ning.billing.util.glue.FieldStoreModule;
 import com.ning.billing.util.glue.GlobalLockerModule;
@@ -51,7 +52,7 @@ import com.ning.billing.util.glue.TagStoreModule;
 import com.ning.billing.util.notificationq.MockNotificationQueueService;
 import com.ning.billing.util.notificationq.NotificationQueueService;
 
-public class InvoiceModuleWithEmbeddedDb extends InvoiceModule {
+public class InvoiceModuleWithEmbeddedDb extends DefaultInvoiceModule {
     private final MysqlTestingHelper helper = new MysqlTestingHelper();
     private IDBI dbi;
 
@@ -111,13 +112,15 @@ public class InvoiceModuleWithEmbeddedDb extends InvoiceModule {
         bind(BillingApi.class).toInstance(billingApi);
 
         install(new CatalogModule());
-
+        install(new MockEntitlementModule());
         install(new GlobalLockerModule());
 
         super.configure();
 
         bind(InvoiceTestApi.class).to(DefaultInvoiceTestApi.class).asEagerSingleton();
         install(new BusModule());
+        install(new TemplateModule());
+
     }
 
     private static void loadSystemPropertiesFromClasspath(final String resource) {
diff --git a/invoice/src/test/java/com/ning/billing/invoice/glue/InvoiceModuleWithMocks.java b/invoice/src/test/java/com/ning/billing/invoice/glue/InvoiceModuleWithMocks.java
index 17e732b..bd43d0a 100644
--- a/invoice/src/test/java/com/ning/billing/invoice/glue/InvoiceModuleWithMocks.java
+++ b/invoice/src/test/java/com/ning/billing/invoice/glue/InvoiceModuleWithMocks.java
@@ -25,8 +25,8 @@ import com.ning.billing.util.globallocker.MockGlobalLocker;
 import com.ning.billing.util.glue.FieldStoreModule;
 
 
-public class InvoiceModuleWithMocks extends InvoiceModule {
-    @Override
+public class InvoiceModuleWithMocks extends DefaultInvoiceModule {
+    @Override 
     protected void installInvoiceDao() {
         bind(MockInvoiceDao.class).asEagerSingleton();
         bind(InvoiceDao.class).to(MockInvoiceDao.class);
@@ -49,7 +49,7 @@ public class InvoiceModuleWithMocks extends InvoiceModule {
     }
 
     @Override
-    protected void installInvoiceMigrationApi() {
+    public void installInvoiceMigrationApi() {
 
     }
 
diff --git a/invoice/src/test/java/com/ning/billing/invoice/MockModule.java b/invoice/src/test/java/com/ning/billing/invoice/MockModule.java
index 521069c..c756020 100644
--- a/invoice/src/test/java/com/ning/billing/invoice/MockModule.java
+++ b/invoice/src/test/java/com/ning/billing/invoice/MockModule.java
@@ -16,9 +16,6 @@
 
 package com.ning.billing.invoice;
 
-import com.ning.billing.invoice.api.formatters.InvoiceFormatterFactory;
-import com.ning.billing.invoice.template.formatters.DefaultInvoiceFormatterFactory;
-import com.ning.billing.util.email.EmailModule;
 import org.skife.config.ConfigurationObjectFactory;
 import org.skife.jdbi.v2.IDBI;
 
@@ -27,12 +24,16 @@ import com.ning.billing.catalog.glue.CatalogModule;
 import com.ning.billing.dbi.DBIProvider;
 import com.ning.billing.dbi.DbiConfig;
 import com.ning.billing.dbi.MysqlTestingHelper;
-import com.ning.billing.invoice.glue.InvoiceModule;
+import com.ning.billing.invoice.api.formatters.InvoiceFormatterFactory;
+import com.ning.billing.invoice.glue.DefaultInvoiceModule;
+import com.ning.billing.invoice.template.formatters.DefaultInvoiceFormatterFactory;
 import com.ning.billing.mock.glue.MockJunctionModule;
 import com.ning.billing.util.callcontext.CallContextFactory;
 import com.ning.billing.util.callcontext.DefaultCallContextFactory;
 import com.ning.billing.util.clock.Clock;
 import com.ning.billing.util.clock.ClockMock;
+import com.ning.billing.util.email.EmailModule;
+import com.ning.billing.util.email.templates.TemplateModule;
 import com.ning.billing.util.glue.BusModule;
 import com.ning.billing.util.glue.FieldStoreModule;
 import com.ning.billing.util.glue.GlobalLockerModule;
@@ -68,9 +69,11 @@ public class MockModule extends AbstractModule {
         install(new BusModule());
         installInvoiceModule();
         install(new MockJunctionModule());
+        install(new TemplateModule());
+
     }
 
     protected void installInvoiceModule() {
-    	install(new InvoiceModule());
+    	install(new DefaultInvoiceModule());
     }
 }
diff --git a/invoice/src/test/java/com/ning/billing/invoice/notification/TestNextBillingDateNotifier.java b/invoice/src/test/java/com/ning/billing/invoice/notification/TestNextBillingDateNotifier.java
index 1242aee..a0eb6d5 100644
--- a/invoice/src/test/java/com/ning/billing/invoice/notification/TestNextBillingDateNotifier.java
+++ b/invoice/src/test/java/com/ning/billing/invoice/notification/TestNextBillingDateNotifier.java
@@ -40,54 +40,32 @@ import com.google.inject.AbstractModule;
 import com.google.inject.Guice;
 import com.google.inject.Injector;
 import com.google.inject.Stage;
-import com.google.inject.name.Names;
-import com.ning.billing.catalog.DefaultCatalogService;
-import com.ning.billing.catalog.api.CatalogService;
-import com.ning.billing.config.CatalogConfig;
+import com.ning.billing.catalog.MockCatalogModule;
 import com.ning.billing.config.InvoiceConfig;
 import com.ning.billing.dbi.DBIProvider;
 import com.ning.billing.dbi.DbiConfig;
 import com.ning.billing.dbi.MysqlTestingHelper;
-import com.ning.billing.entitlement.api.SubscriptionApiService;
-import com.ning.billing.entitlement.api.SubscriptionFactory;
-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.user.DefaultSubscriptionApiService;
-import com.ning.billing.entitlement.api.user.DefaultSubscriptionFactory;
 import com.ning.billing.entitlement.api.user.EntitlementUserApi;
 import com.ning.billing.entitlement.api.user.Subscription;
-import com.ning.billing.entitlement.engine.dao.EntitlementDao;
-import com.ning.billing.entitlement.engine.dao.AuditedEntitlementDao;
-import com.ning.billing.entitlement.engine.dao.RepairEntitlementDao;
-import com.ning.billing.entitlement.glue.EntitlementModule;
 import com.ning.billing.invoice.InvoiceDispatcher;
 import com.ning.billing.invoice.InvoiceListener;
-import com.ning.billing.invoice.api.InvoiceNotifier;
-import com.ning.billing.invoice.dao.DefaultInvoiceDao;
-import com.ning.billing.invoice.dao.InvoiceDao;
-import com.ning.billing.invoice.model.DefaultInvoiceGenerator;
-import com.ning.billing.invoice.model.InvoiceGenerator;
+import com.ning.billing.invoice.glue.InvoiceModuleWithMocks;
 import com.ning.billing.lifecycle.KillbillService;
 import com.ning.billing.mock.BrainDeadProxyFactory;
 import com.ning.billing.mock.BrainDeadProxyFactory.ZombieControl;
 import com.ning.billing.mock.glue.MockJunctionModule;
 import com.ning.billing.util.bus.Bus;
-import com.ning.billing.util.bus.InMemoryBus;
 import com.ning.billing.util.callcontext.CallContextFactory;
 import com.ning.billing.util.callcontext.DefaultCallContextFactory;
 import com.ning.billing.util.clock.Clock;
 import com.ning.billing.util.clock.ClockMock;
-import com.ning.billing.util.customfield.dao.AuditedCustomFieldDao;
-import com.ning.billing.util.customfield.dao.CustomFieldDao;
-import com.ning.billing.util.globallocker.GlobalLocker;
-import com.ning.billing.util.globallocker.MySqlGlobalLocker;
-import com.ning.billing.util.notificationq.DefaultNotificationQueueService;
+import com.ning.billing.util.clock.MockClockModule;
+import com.ning.billing.util.glue.BusModule;
+import com.ning.billing.util.glue.BusModule.BusType;
+import com.ning.billing.util.glue.NotificationQueueModule;
 import com.ning.billing.util.notificationq.DummySqlTest;
 import com.ning.billing.util.notificationq.NotificationQueueService;
 import com.ning.billing.util.notificationq.dao.NotificationSqlDao;
-import com.ning.billing.util.tag.dao.AuditedTagDao;
-import com.ning.billing.util.tag.dao.TagDao;
 
 public class TestNextBillingDateNotifier {
 	private Clock clock;
@@ -135,16 +113,12 @@ public class TestNextBillingDateNotifier {
         final Injector g = Guice.createInjector(Stage.PRODUCTION,  new AbstractModule() {
 			
             protected void configure() {
-                bind(Clock.class).to(ClockMock.class).asEagerSingleton();
-                bind(CallContextFactory.class).to(DefaultCallContextFactory.class).asEagerSingleton();
-                bind(Bus.class).to(InMemoryBus.class).asEagerSingleton();
-                bind(NotificationQueueService.class).to(DefaultNotificationQueueService.class).asEagerSingleton();
-                bind(InvoiceNotifier.class).to(NullInvoiceNotifier.class).asEagerSingleton();
-                final InvoiceConfig invoiceConfig = new ConfigurationObjectFactory(System.getProperties()).build(InvoiceConfig.class);
-                bind(InvoiceConfig.class).toInstance(invoiceConfig);
-                final CatalogConfig catalogConfig = new ConfigurationObjectFactory(System.getProperties()).build(CatalogConfig.class);
-                bind(CatalogConfig.class).toInstance(catalogConfig);
-                bind(CatalogService.class).to(DefaultCatalogService.class).asEagerSingleton();
+                install(new MockClockModule());
+                install(new BusModule(BusType.MEMORY));
+                install(new InvoiceModuleWithMocks());
+                install(new MockJunctionModule());
+                install(new MockCatalogModule());
+                install(new NotificationQueueModule());
                 
                 final MysqlTestingHelper helper = new MysqlTestingHelper();
                 bind(MysqlTestingHelper.class).toInstance(helper);
@@ -157,22 +131,7 @@ public class TestNextBillingDateNotifier {
                     bind(IDBI.class).toInstance(dbi);
                 }
                 
-                bind(TagDao.class).to(AuditedTagDao.class).asEagerSingleton();
-                bind(EntitlementDao.class).to(AuditedEntitlementDao.class).asEagerSingleton();
-                bind(EntitlementDao.class).annotatedWith(Names.named(EntitlementModule.REPAIR_NAMED)).to(RepairEntitlementDao.class);
-                bind(RepairEntitlementLifecycleDao.class).annotatedWith(Names.named(EntitlementModule.REPAIR_NAMED)).to(RepairEntitlementDao.class);
-                bind(RepairEntitlementDao.class).asEagerSingleton();
-                bind(CustomFieldDao.class).to(AuditedCustomFieldDao.class).asEagerSingleton();
-                bind(GlobalLocker.class).to(MySqlGlobalLocker.class).asEagerSingleton();
-                bind(InvoiceGenerator.class).to(DefaultInvoiceGenerator.class).asEagerSingleton();
-                bind(InvoiceDao.class).to(DefaultInvoiceDao.class).asEagerSingleton();
-                bind(NextBillingDatePoster.class).to(DefaultNextBillingDatePoster.class).asEagerSingleton();
-                bind(SubscriptionApiService.class).annotatedWith(Names.named(EntitlementModule.REPAIR_NAMED)).to(RepairSubscriptionApiService.class).asEagerSingleton();
-                bind(SubscriptionApiService.class).to(DefaultSubscriptionApiService.class).asEagerSingleton();
-                bind(SubscriptionFactory.class).annotatedWith(Names.named(EntitlementModule.REPAIR_NAMED)).to(RepairSubscriptionFactory.class).asEagerSingleton();
-                bind(SubscriptionFactory.class).to(DefaultSubscriptionFactory.class).asEagerSingleton();
             
-                install(new MockJunctionModule());
             }
         });
 
@@ -204,7 +163,7 @@ public class TestNextBillingDateNotifier {
 
 
 	@Test(enabled=true, groups="slow")
-	public void test() throws Exception {
+	public void testInvoiceNotifier() throws Exception {
 		final UUID subscriptionId = new UUID(0L,1L);
 		final DateTime now = new DateTime();
 		final DateTime readyTime = now.plusMillis(2000);
diff --git a/invoice/src/test/java/com/ning/billing/invoice/TestInvoiceDispatcher.java b/invoice/src/test/java/com/ning/billing/invoice/TestInvoiceDispatcher.java
index e591dfa..26403d0 100644
--- a/invoice/src/test/java/com/ning/billing/invoice/TestInvoiceDispatcher.java
+++ b/invoice/src/test/java/com/ning/billing/invoice/TestInvoiceDispatcher.java
@@ -115,7 +115,7 @@ public class TestInvoiceDispatcher extends InvoicingTestBase {
         context = new DefaultCallContextFactory(clock).createCallContext("Miracle Max", CallOrigin.TEST, UserType.TEST);
 
 		busService.getBus().start();
-		((ZombieControl)billingApi).addResult("setChargedThroughDateFromTransaction", BrainDeadProxyFactory.ZOMBIE_VOID);
+		((ZombieControl)billingApi).addResult("setChargedThroughDate", BrainDeadProxyFactory.ZOMBIE_VOID);
 	}
 
 	@AfterClass(alwaysRun = true)
@@ -155,13 +155,12 @@ public class TestInvoiceDispatcher extends InvoicingTestBase {
                 fixedPrice, BigDecimal.ONE, currency, BillingPeriod.MONTHLY, 1,
                 BillingModeType.IN_ADVANCE, "", 1L, SubscriptionTransitionType.CREATE));
 
-		BillingApi entitlementBillingApi = BrainDeadProxyFactory.createBrainDeadProxyFor(BillingApi.class);
-		((ZombieControl) entitlementBillingApi).addResult("getBillingEventsForAccountAndUpdateAccountBCD", events);
+		((ZombieControl) billingApi).addResult("getBillingEventsForAccountAndUpdateAccountBCD", events);
 
 		DateTime target = new DateTime();
 
         InvoiceNotifier invoiceNotifier = new NullInvoiceNotifier();
-		InvoiceDispatcher dispatcher = new InvoiceDispatcher(generator, accountUserApi, entitlementBillingApi, invoiceDao,
+		InvoiceDispatcher dispatcher = new InvoiceDispatcher(generator, accountUserApi, billingApi, invoiceDao,
                                                              invoiceNotifier, locker, busService.getBus(), clock);
 
 		Invoice invoice = dispatcher.processAccount(accountId, target, true, context);
diff --git a/invoice/src/test/java/com/ning/billing/invoice/tests/DefaultInvoiceGeneratorTests.java b/invoice/src/test/java/com/ning/billing/invoice/tests/DefaultInvoiceGeneratorTests.java
index 49fca25..1aaab2d 100644
--- a/invoice/src/test/java/com/ning/billing/invoice/tests/DefaultInvoiceGeneratorTests.java
+++ b/invoice/src/test/java/com/ning/billing/invoice/tests/DefaultInvoiceGeneratorTests.java
@@ -22,13 +22,11 @@ import static org.testng.Assert.assertNull;
 
 import java.math.BigDecimal;
 import java.util.ArrayList;
-import java.util.Iterator;
 import java.util.List;
 import java.util.UUID;
 
 import javax.annotation.Nullable;
 
-import com.ning.billing.invoice.api.InvoiceItem;
 import com.ning.billing.invoice.model.DefaultInvoicePayment;
 import org.joda.time.DateTime;
 import org.testng.annotations.Test;
diff --git a/jaxrs/src/main/java/com/ning/billing/jaxrs/json/AccountTimelineJson.java b/jaxrs/src/main/java/com/ning/billing/jaxrs/json/AccountTimelineJson.java
index 1241b7d..8c242d0 100644
--- a/jaxrs/src/main/java/com/ning/billing/jaxrs/json/AccountTimelineJson.java
+++ b/jaxrs/src/main/java/com/ning/billing/jaxrs/json/AccountTimelineJson.java
@@ -15,6 +15,7 @@
  */
 package com.ning.billing.jaxrs.json;
 
+import java.math.BigDecimal;
 import java.util.LinkedList;
 import java.util.List;
 
@@ -25,7 +26,7 @@ import org.codehaus.jackson.map.annotate.JsonView;
 import com.ning.billing.account.api.Account;
 import com.ning.billing.entitlement.api.timeline.BundleTimeline;
 import com.ning.billing.invoice.api.Invoice;
-import com.ning.billing.payment.api.PaymentInfoEvent;
+import com.ning.billing.payment.api.PaymentAttempt;
 
 public class AccountTimelineJson {
 
@@ -52,7 +53,7 @@ public class AccountTimelineJson {
         this.payments = payments;
     }
     
-    public AccountTimelineJson(Account account, List<Invoice> invoices, List<PaymentInfoEvent> payments, List<BundleTimeline> bundles) {
+    public AccountTimelineJson(Account account, List<Invoice> invoices, List<PaymentAttempt> payments, List<BundleTimeline> bundles) {
         this.account = new AccountJsonSimple(account.getId().toString(), account.getExternalKey());
         this.bundles = new LinkedList<BundleJsonWithSubscriptions>();
         for (BundleTimeline cur : bundles) {
@@ -63,10 +64,13 @@ public class AccountTimelineJson {
             this.invoices.add(new InvoiceJson(cur.getTotalAmount(), cur.getId().toString(), cur.getInvoiceDate(), Integer.toString(cur.getInvoiceNumber()), cur.getBalance()));
         }
         this.payments = new LinkedList<PaymentJson>();
-        for (PaymentInfoEvent cur : payments) {
-            // STEPH how to link that payment with the invoice ??
-            this.payments.add(new PaymentJson(cur.getAmount(), null , cur.getPaymentNumber(), null, cur.getEffectiveDate(), cur.getStatus()));
-        }
+        for (PaymentAttempt cur : payments) {
+            String status = cur.getPaymentId() != null ? "Success" : "Failed";
+            BigDecimal paidAmount = cur.getPaymentId() != null ? cur.getAmount() : BigDecimal.ZERO;
+            
+            this.payments.add(new PaymentJson(cur.getAmount(), paidAmount, cur.getInvoiceId(), cur.getPaymentId(), cur.getCreatedDate(), cur.getUpdatedDate(),
+                    cur.getRetryCount(), cur.getCurrency().toString(), status));
+          }
     }
     
     public AccountTimelineJson() {
diff --git a/jaxrs/src/main/java/com/ning/billing/jaxrs/json/BundleJsonNoSubsciptions.java b/jaxrs/src/main/java/com/ning/billing/jaxrs/json/BundleJsonNoSubsciptions.java
index 9f8f7b6..753688b 100644
--- a/jaxrs/src/main/java/com/ning/billing/jaxrs/json/BundleJsonNoSubsciptions.java
+++ b/jaxrs/src/main/java/com/ning/billing/jaxrs/json/BundleJsonNoSubsciptions.java
@@ -16,16 +16,12 @@
 
 package com.ning.billing.jaxrs.json;
 
-import java.util.LinkedList;
 import java.util.List;
-import java.util.UUID;
 
 import org.codehaus.jackson.annotate.JsonCreator;
 import org.codehaus.jackson.annotate.JsonProperty;
 import org.codehaus.jackson.map.annotate.JsonView;
 
-import com.ning.billing.entitlement.api.timeline.BundleTimeline;
-import com.ning.billing.entitlement.api.timeline.SubscriptionTimeline;
 import com.ning.billing.entitlement.api.user.SubscriptionBundle;
 
 public class BundleJsonNoSubsciptions  extends BundleJsonSimple {
@@ -49,7 +45,7 @@ public class BundleJsonNoSubsciptions  extends BundleJsonSimple {
 
     
     public BundleJsonNoSubsciptions(SubscriptionBundle bundle) {
-        super(bundle.getId().toString(), bundle.getKey());        
+        super(bundle.getId().toString(), bundle.getKey());
         this.accountId = bundle.getAccountId().toString();
     }
     
diff --git a/jaxrs/src/main/java/com/ning/billing/jaxrs/json/BundleJsonWithSubscriptions.java b/jaxrs/src/main/java/com/ning/billing/jaxrs/json/BundleJsonWithSubscriptions.java
index dd2c788..a51e09e 100644
--- a/jaxrs/src/main/java/com/ning/billing/jaxrs/json/BundleJsonWithSubscriptions.java
+++ b/jaxrs/src/main/java/com/ning/billing/jaxrs/json/BundleJsonWithSubscriptions.java
@@ -53,7 +53,7 @@ public class BundleJsonWithSubscriptions extends BundleJsonSimple {
     }
     
     public BundleJsonWithSubscriptions(SubscriptionBundle bundle) {
-        super(bundle.getId().toString(), bundle.getKey());        
+        super(bundle.getId().toString(), bundle.getKey());
         this.subscriptions = null;
     }
     
diff --git a/jaxrs/src/main/java/com/ning/billing/jaxrs/json/CustomFieldJson.java b/jaxrs/src/main/java/com/ning/billing/jaxrs/json/CustomFieldJson.java
new file mode 100644
index 0000000..cb9e36b
--- /dev/null
+++ b/jaxrs/src/main/java/com/ning/billing/jaxrs/json/CustomFieldJson.java
@@ -0,0 +1,51 @@
+/* 
+ * 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.jaxrs.json;
+
+import org.codehaus.jackson.annotate.JsonCreator;
+
+import com.ning.billing.util.customfield.CustomField;
+
+public class CustomFieldJson {
+
+    private final String name;
+    private final String value;
+    
+    public CustomFieldJson() {
+        this.name = null;
+        this.value = null;
+    }
+    
+    @JsonCreator
+    public CustomFieldJson(String name, String value) {
+        super();
+        this.name = name;
+        this.value = value;
+    }
+    
+    public CustomFieldJson(CustomField input) {
+        this.name = input.getName();
+        this.value = input.getValue();
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public String getValue() {
+        return value;
+    }
+}
diff --git a/jaxrs/src/main/java/com/ning/billing/jaxrs/json/InvoiceJson.java b/jaxrs/src/main/java/com/ning/billing/jaxrs/json/InvoiceJson.java
index 30264d7..1a33a04 100644
--- a/jaxrs/src/main/java/com/ning/billing/jaxrs/json/InvoiceJson.java
+++ b/jaxrs/src/main/java/com/ning/billing/jaxrs/json/InvoiceJson.java
@@ -23,6 +23,8 @@ import org.codehaus.jackson.annotate.JsonProperty;
 import org.codehaus.jackson.map.annotate.JsonView;
 import org.joda.time.DateTime;
 
+import com.ning.billing.invoice.api.Invoice;
+
 public class InvoiceJson {
 
     @JsonView(BundleTimelineViews.Base.class)
@@ -40,6 +42,15 @@ public class InvoiceJson {
     @JsonView(BundleTimelineViews.Base.class)
     private final BigDecimal balance;
 
+    
+    public InvoiceJson() {
+        this.amount = null;
+        this.invoiceId = null;
+        this.invoiceDate = null;
+        this.invoiceNumber = null;
+        this.balance = null;
+    }
+    
     @JsonCreator
     public InvoiceJson(@JsonProperty("amount") BigDecimal amount,
             @JsonProperty("invoice_id") String invoiceId,
@@ -54,6 +65,14 @@ public class InvoiceJson {
         this.balance = balance;
     }
 
+    public InvoiceJson(Invoice input) {
+        this.amount = input.getTotalAmount();
+        this.invoiceId = input.getId().toString();
+        this.invoiceDate = input.getInvoiceDate();
+        this.invoiceNumber = String.valueOf(input.getInvoiceNumber());
+        this.balance = input.getBalance();
+    }
+    
     public BigDecimal getAmount() {
         return amount;
     }
@@ -73,4 +92,56 @@ public class InvoiceJson {
     public BigDecimal getBalance() {
         return balance;
     }
+
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result + ((amount == null) ? 0 : amount.hashCode());
+        result = prime * result + ((balance == null) ? 0 : balance.hashCode());
+        result = prime * result
+                + ((invoiceDate == null) ? 0 : invoiceDate.hashCode());
+        result = prime * result
+                + ((invoiceId == null) ? 0 : invoiceId.hashCode());
+        result = prime * result
+                + ((invoiceNumber == null) ? 0 : invoiceNumber.hashCode());
+        return result;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj)
+            return true;
+        if (obj == null)
+            return false;
+        if (getClass() != obj.getClass())
+            return false;
+        InvoiceJson other = (InvoiceJson) obj;
+        if (amount == null) {
+            if (other.amount != null)
+                return false;
+        } else if (!amount.equals(other.amount))
+            return false;
+        if (balance == null) {
+            if (other.balance != null)
+                return false;
+        } else if (!balance.equals(other.balance))
+            return false;
+        if (invoiceDate == null) {
+            if (other.invoiceDate != null)
+                return false;
+        } else if (!invoiceDate.equals(other.invoiceDate))
+            return false;
+        if (invoiceId == null) {
+            if (other.invoiceId != null)
+                return false;
+        } else if (!invoiceId.equals(other.invoiceId))
+            return false;
+        if (invoiceNumber == null) {
+            if (other.invoiceNumber != null)
+                return false;
+        } else if (!invoiceNumber.equals(other.invoiceNumber))
+            return false;
+        return true;
+    }
 }
diff --git a/jaxrs/src/main/java/com/ning/billing/jaxrs/json/PaymentJson.java b/jaxrs/src/main/java/com/ning/billing/jaxrs/json/PaymentJson.java
index 3a9fd4d..5d2a3a0 100644
--- a/jaxrs/src/main/java/com/ning/billing/jaxrs/json/PaymentJson.java
+++ b/jaxrs/src/main/java/com/ning/billing/jaxrs/json/PaymentJson.java
@@ -17,6 +17,7 @@
 package com.ning.billing.jaxrs.json;
 
 import java.math.BigDecimal;
+import java.util.UUID;
 
 import org.codehaus.jackson.annotate.JsonCreator;
 import org.codehaus.jackson.annotate.JsonProperty;
@@ -27,37 +28,55 @@ import com.ning.billing.util.clock.DefaultClock;
 
 public class PaymentJson {
 
-    @JsonView(BundleTimelineViews.Base.class)
     private final BigDecimal paidAmount;
+
+    private final BigDecimal amount;
+
+    private final UUID invoiceId;
     
-    @JsonView(BundleTimelineViews.Base.class)
-    private final String invoiceId;
-    
-    @JsonView(BundleTimelineViews.Base.class)
-    private final String paymentId;
+    private final UUID paymentId;
     
-    @JsonView(BundleTimelineViews.Base.class)
     private final DateTime requestedDate;
     
-    @JsonView(BundleTimelineViews.Base.class)
     private final DateTime effectiveDate;
     
-    @JsonView(BundleTimelineViews.Base.class)
+    private final Integer retryCount;
+    
+    private final String currency;
+    
     private final String status;
+      
+    public PaymentJson() {
+        this.amount = null;
+        this.paidAmount = null;
+        this.invoiceId = null;
+        this.paymentId = null;
+        this.requestedDate = null;
+        this.effectiveDate = null;
+        this.currency = null;
+        this.retryCount = null;
+        this.status = null;
+    }
 
     @JsonCreator
-    public PaymentJson(@JsonProperty("paid_amount") BigDecimal paidAmount,
-            @JsonProperty("invoice_id") String invoiceId,
-            @JsonProperty("payment_id") String paymentId,
+    public PaymentJson(@JsonProperty("amount") BigDecimal amount,
+            @JsonProperty("paid_amount") BigDecimal paidAmount,
+            @JsonProperty("invoice_id") UUID invoiceId,
+            @JsonProperty("payment_id") UUID paymentId,
             @JsonProperty("requested_dt") DateTime requestedDate,
             @JsonProperty("effective_dt") DateTime effectiveDate,
+            @JsonProperty("retry_count") Integer retryCount,
+            @JsonProperty("currency") String currency,            
             @JsonProperty("status") String status) {
         super();
+        this.amount = amount;
         this.paidAmount = paidAmount;
         this.invoiceId = invoiceId;
         this.paymentId = paymentId;
         this.requestedDate = DefaultClock.toUTCDateTime(requestedDate);
         this.effectiveDate = DefaultClock.toUTCDateTime(effectiveDate);
+        this.currency = currency;
+        this.retryCount = retryCount;
         this.status = status;
     }
 
@@ -65,11 +84,11 @@ public class PaymentJson {
         return paidAmount;
     }
 
-    public String getInvoiceId() {
+    public UUID getInvoiceId() {
         return invoiceId;
     }
 
-    public String getPaymentId() {
+    public UUID getPaymentId() {
         return paymentId;
     }
 
@@ -84,4 +103,16 @@ public class PaymentJson {
     public String getStatus() {
         return status;
     }
+
+    public BigDecimal getAmount() {
+        return amount;
+    }
+
+    public Integer getRetryCount() {
+        return retryCount;
+    }
+
+    public String getCurrency() {
+        return currency;
+    }
 }
diff --git a/jaxrs/src/main/java/com/ning/billing/jaxrs/json/SubscriptionJsonWithEvents.java b/jaxrs/src/main/java/com/ning/billing/jaxrs/json/SubscriptionJsonWithEvents.java
index fea22e6..2f798df 100644
--- a/jaxrs/src/main/java/com/ning/billing/jaxrs/json/SubscriptionJsonWithEvents.java
+++ b/jaxrs/src/main/java/com/ning/billing/jaxrs/json/SubscriptionJsonWithEvents.java
@@ -51,6 +51,12 @@ public class SubscriptionJsonWithEvents extends SubscriptionJsonSimple {
         @JsonView(BundleTimelineViews.Timeline.class)
         private final DateTime effectiveDate;
 
+        public SubscriptionReadEventJson() {
+            super();
+            this.eventId = null;
+            this.effectiveDate = null;
+        }
+ 
         @JsonCreator
         public SubscriptionReadEventJson(@JsonProperty("event_id") String eventId,
                 @JsonProperty("billing_period") String billingPeriod,
@@ -148,6 +154,15 @@ public class SubscriptionJsonWithEvents extends SubscriptionJsonSimple {
         @JsonView(BundleTimelineViews.Timeline.class)
         private final String phase;
 
+        public SubscriptionBaseEventJson() {
+            this.billingPeriod = null;
+            this.requestedDate = null;
+            this.product = null;
+            this.priceList = null;
+            this.eventType = null;
+            this.phase = null;
+        }
+        
         @JsonCreator
         public SubscriptionBaseEventJson(@JsonProperty("billing_period") String billingPeriod,
                 @JsonProperty("requested_dt") DateTime requestedDate,
diff --git a/jaxrs/src/main/java/com/ning/billing/jaxrs/json/TagDefinitionJson.java b/jaxrs/src/main/java/com/ning/billing/jaxrs/json/TagDefinitionJson.java
new file mode 100644
index 0000000..25ef04c
--- /dev/null
+++ b/jaxrs/src/main/java/com/ning/billing/jaxrs/json/TagDefinitionJson.java
@@ -0,0 +1,77 @@
+/* 
+ * 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.jaxrs.json;
+
+import org.codehaus.jackson.annotate.JsonCreator;
+import org.codehaus.jackson.annotate.JsonProperty;
+
+public class TagDefinitionJson {
+    
+    private final String name;
+    private final String description;
+
+    public TagDefinitionJson()  {
+        this.name = null;
+        this.description = null;
+    }
+    
+    @JsonCreator
+    public TagDefinitionJson(@JsonProperty("name") String name,
+            @JsonProperty("description") String description) {
+        super();
+        this.name = name;
+        this.description = description;
+    }
+    
+    public String getName() {
+        return name;
+    }
+    public String getDescription() {
+        return description;
+    }
+
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result
+                + ((description == null) ? 0 : description.hashCode());
+        result = prime * result + ((name == null) ? 0 : name.hashCode());
+        return result;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj)
+            return true;
+        if (obj == null)
+            return false;
+        if (getClass() != obj.getClass())
+            return false;
+        TagDefinitionJson other = (TagDefinitionJson) obj;
+        if (description == null) {
+            if (other.description != null)
+                return false;
+        } else if (!description.equals(other.description))
+            return false;
+        if (name == null) {
+            if (other.name != null)
+                return false;
+        } else if (!name.equals(other.name))
+            return false;
+        return true;
+    }
+}
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 ed0a116..92aafb3 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
@@ -18,7 +18,7 @@ package com.ning.billing.jaxrs.resources;
 
 import static javax.ws.rs.core.MediaType.APPLICATION_JSON;
 
-import java.net.URI;
+
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
@@ -29,6 +29,7 @@ import java.util.UUID;
 import javax.ws.rs.Consumes;
 import javax.ws.rs.DELETE;
 import javax.ws.rs.GET;
+import javax.ws.rs.HeaderParam;
 import javax.ws.rs.POST;
 import javax.ws.rs.PUT;
 import javax.ws.rs.Path;
@@ -37,12 +38,12 @@ import javax.ws.rs.Produces;
 import javax.ws.rs.QueryParam;
 import javax.ws.rs.core.Response;
 import javax.ws.rs.core.Response.Status;
-import javax.ws.rs.core.UriBuilder;
 
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import com.google.common.base.Function;
+import com.google.common.base.Preconditions;
 import com.google.common.collect.Collections2;
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
@@ -61,10 +62,19 @@ import com.ning.billing.invoice.api.InvoiceUserApi;
 import com.ning.billing.jaxrs.json.AccountJson;
 import com.ning.billing.jaxrs.json.AccountTimelineJson;
 import com.ning.billing.jaxrs.json.BundleJsonNoSubsciptions;
+import com.ning.billing.jaxrs.json.CustomFieldJson;
 import com.ning.billing.jaxrs.util.Context;
 import com.ning.billing.jaxrs.util.JaxrsUriBuilder;
+import com.ning.billing.jaxrs.util.TagHelper;
 import com.ning.billing.payment.api.PaymentApi;
-import com.ning.billing.payment.api.PaymentInfoEvent;
+import com.ning.billing.payment.api.PaymentApiException;
+import com.ning.billing.payment.api.PaymentAttempt;
+import com.ning.billing.util.api.TagDefinitionApiException;
+import com.ning.billing.util.api.TagUserApi;
+import com.ning.billing.util.customfield.CustomField;
+import com.ning.billing.util.customfield.StringCustomField;
+import com.ning.billing.util.tag.Tag;
+import com.ning.billing.util.tag.TagDefinition;
 
 
 @Singleton
@@ -79,8 +89,10 @@ public class AccountResource implements BaseJaxrsResource {
     private final InvoiceUserApi invoiceApi;
     private final PaymentApi paymentApi;
     private final Context context;
+    private final TagUserApi tagUserApi;
     private final JaxrsUriBuilder uriBuilder;
-
+    private final TagHelper tagHelper;
+    
     @Inject
     public AccountResource(final JaxrsUriBuilder uriBuilder,
             final AccountUserApi accountApi,
@@ -88,14 +100,18 @@ public class AccountResource implements BaseJaxrsResource {
             final InvoiceUserApi invoiceApi,
             final PaymentApi paymentApi,
             final EntitlementTimelineApi timelineApi,
+            final TagUserApi tagUserApi,
+            final TagHelper tagHelper,
             final Context context) {
         this.uriBuilder = uriBuilder;
     	this.accountApi = accountApi;
+    	this.tagUserApi = tagUserApi;
         this.entitlementApi = entitlementApi;
         this.invoiceApi = invoiceApi;
         this.paymentApi = paymentApi;
         this.timelineApi = timelineApi;
         this.context = context;
+        this.tagHelper = tagHelper;
     }
 
     @GET
@@ -108,7 +124,6 @@ public class AccountResource implements BaseJaxrsResource {
             AccountJson json = new AccountJson(account);
             return Response.status(Status.OK).entity(json).build();
         } catch (AccountApiException e) {
-            log.warn("Failed to find account.", e);
             return Response.status(Status.NO_CONTENT).build();            
         }
         
@@ -131,7 +146,6 @@ public class AccountResource implements BaseJaxrsResource {
             });
             return Response.status(Status.OK).entity(result).build();
         } catch (AccountApiException e) {
-            log.warn("Failed to find account.", e);
             return Response.status(Status.NO_CONTENT).build();
         }
     }
@@ -151,7 +165,6 @@ public class AccountResource implements BaseJaxrsResource {
             AccountJson json = new AccountJson(account);
             return Response.status(Status.OK).entity(json).build();
         } catch (AccountApiException e) {
-            log.warn("Failed to find account.", e);
             return Response.status(Status.NO_CONTENT).build();
         }
     }
@@ -160,17 +173,22 @@ public class AccountResource implements BaseJaxrsResource {
     @POST
     @Consumes(APPLICATION_JSON)
     @Produces(APPLICATION_JSON)
-    public Response createAccount(AccountJson json) {
+    public Response createAccount(final AccountJson json,
+            @HeaderParam(HDR_CREATED_BY) final String createdBy,
+            @HeaderParam(HDR_REASON) final String reason,
+            @HeaderParam(HDR_COMMENT) final String comment) {
 
         try {
-        	
             AccountData data = json.toAccountData();
-            final Account account = accountApi.createAccount(data, null, null, context.createContext());
-            URI uri = UriBuilder.fromPath(account.getId().toString()).build();
-            return uriBuilder.buildResponse(AccountResource.class, "getAccount", account.getId());
+            final Account account = accountApi.createAccount(data, null, null, context.createContext(createdBy, reason, comment));
+            Response response = uriBuilder.buildResponse(AccountResource.class, "getAccount", account.getId());
+            return response;
         } catch (AccountApiException e) {
-            log.info(String.format("Failed to create account %s", json), e);
-            return Response.status(Status.BAD_REQUEST).build();
+            final String error = String.format("Failed to create account %s", json);
+            log.info(error, e);
+            return Response.status(Status.BAD_REQUEST).entity(error).build();
+        } catch (IllegalArgumentException e) {
+            return Response.status(Status.BAD_REQUEST).entity(e.getMessage()).build();
         }
     }
 
@@ -178,11 +196,15 @@ public class AccountResource implements BaseJaxrsResource {
     @Consumes(APPLICATION_JSON)
     @Produces(APPLICATION_JSON)
     @Path("/{accountId:" + UUID_PATTERN + "}")
-    public Response updateAccount(AccountJson json, @PathParam("accountId") String accountId) {
+    public Response updateAccount(final AccountJson json,
+            @PathParam("accountId") final String accountId,
+            @HeaderParam(HDR_CREATED_BY) final String createdBy,
+            @HeaderParam(HDR_REASON) final String reason,
+            @HeaderParam(HDR_COMMENT) final String comment) {
         try {
             AccountData data = json.toAccountData();
             UUID uuid = UUID.fromString(accountId);
-            accountApi.updateAccount(uuid, data, context.createContext());
+            accountApi.updateAccount(uuid, data, context.createContext(createdBy, reason, comment));
             return getAccount(accountId);
         } catch (AccountApiException e) {
         	if (e.getCode() == ErrorCode.ACCOUNT_DOES_NOT_EXIST_FOR_ID.getCode()) {
@@ -191,6 +213,8 @@ public class AccountResource implements BaseJaxrsResource {
         		log.info(String.format("Failed to update account %s with %s", accountId, json), e);
         		return Response.status(Status.BAD_REQUEST).build();
         	}
+        } catch (IllegalArgumentException e) {
+            return Response.status(Status.BAD_REQUEST).entity(e.getMessage()).build();
         }
     }
 
@@ -219,7 +243,7 @@ public class AccountResource implements BaseJaxrsResource {
             Account account = accountApi.getAccountById(UUID.fromString(accountId));
            
             List<Invoice> invoices = invoiceApi.getInvoicesByAccount(account.getId());
-            List<PaymentInfoEvent> payments = Collections.emptyList();
+            List<PaymentAttempt> payments = new LinkedList<PaymentAttempt>();
 
             if (invoices.size() > 0) {
                 Collection<String> tmp = Collections2.transform(invoices, new Function<Invoice, String>() {
@@ -230,8 +254,9 @@ public class AccountResource implements BaseJaxrsResource {
                 });
                 List<String> invoicesId = new ArrayList<String>();
                 invoicesId.addAll(tmp);
-
-                payments = paymentApi.getPaymentInfoList(invoicesId);
+                for (String curId : invoicesId) {
+                    payments.addAll(paymentApi.getPaymentAttemptsForInvoiceId(curId));
+                }
             }
 
             List<SubscriptionBundle> bundles = entitlementApi.getBundlesForAccount(account.getId());
@@ -242,11 +267,183 @@ public class AccountResource implements BaseJaxrsResource {
             AccountTimelineJson json = new AccountTimelineJson(account, invoices, payments, bundlesTimeline);
             return Response.status(Status.OK).entity(json).build();
         } catch (AccountApiException e) {
-            log.warn("Failed to find account.", e);
             return Response.status(Status.NO_CONTENT).build();
+        } catch (PaymentApiException e) {
+            log.error(e.getMessage());
+            return Response.status(Status.INTERNAL_SERVER_ERROR).build();
         } catch (EntitlementRepairException e) {
             log.error(e.getMessage());
             return Response.status(Status.INTERNAL_SERVER_ERROR).build();
         }
     }
+    
+    
+    /****************************      TAGS     ******************************/
+    
+    @GET
+    @Path(BaseJaxrsResource.TAGS + "/{accountId:" + UUID_PATTERN + "}")
+    @Produces(APPLICATION_JSON)
+    public Response getAccountTags(@PathParam("accountId") String accountId) {
+        try {
+            Account account = accountApi.getAccountById(UUID.fromString(accountId));
+            List<Tag> tags = account.getTagList();
+            Collection<String> tagNameList = (tags.size() == 0) ?
+                    Collections.<String>emptyList() :
+                Collections2.transform(tags, new Function<Tag, String>() {
+                @Override
+                public String apply(Tag input) {
+                    return input.getTagDefinitionName();
+                }
+            });
+            return Response.status(Status.OK).entity(tagNameList).build();
+        } catch (AccountApiException e) {
+            return Response.status(Status.NO_CONTENT).build();
+        }
+    }
+
+    
+    @POST
+    @Path(BaseJaxrsResource.TAGS + "/{accountId:" + UUID_PATTERN + "}")    
+    @Consumes(APPLICATION_JSON)
+    @Produces(APPLICATION_JSON)
+    public Response createAccountTag(@PathParam("accountId") final String accountId,
+            @QueryParam(QUERY_TAGS) final String tagList,
+            @HeaderParam(HDR_CREATED_BY) final String createdBy,
+            @HeaderParam(HDR_REASON) final String reason,
+            @HeaderParam(HDR_COMMENT) final String comment) {
+
+        try {
+            Preconditions.checkNotNull(tagList, "Query % list cannot be null", QUERY_TAGS);
+            
+            Account account = accountApi.getAccountById(UUID.fromString(accountId));
+
+            List<TagDefinition> input = tagHelper.getTagDifinitionFromTagList(tagList);
+            account.addTagsFromDefinitions(input);
+            Response response = uriBuilder.buildResponse(AccountResource.class, "getAccountTags", account.getId());
+            return response;
+        } catch (AccountApiException e) {
+            return Response.status(Status.NO_CONTENT).build();
+        } catch (IllegalArgumentException e) {
+            return Response.status(Status.BAD_REQUEST).entity(e.getMessage()).build();
+        } catch (NullPointerException e) {
+            return Response.status(Status.BAD_REQUEST).entity(e.getMessage()).build();
+        } catch (TagDefinitionApiException e) {
+            return Response.status(Status.BAD_REQUEST).entity(e.getMessage()).build();
+        }
+    }
+    
+    @DELETE
+    @Path(BaseJaxrsResource.TAGS +  "/{accountId:" + UUID_PATTERN + "}")    
+    @Consumes(APPLICATION_JSON)
+    @Produces(APPLICATION_JSON)
+    public Response deleteAccountTag(@PathParam("accountId") final String accountId,
+            @QueryParam(QUERY_TAGS) final String tagList,
+            @HeaderParam(HDR_CREATED_BY) final String createdBy,
+            @HeaderParam(HDR_REASON) final String reason,
+            @HeaderParam(HDR_COMMENT) final String comment) {
+
+        try {
+            Account account = accountApi.getAccountById(UUID.fromString(accountId));
+
+            // Tag APIs needs tome rework...
+            String inputTagList = tagList;
+            if (inputTagList == null) {
+                List<Tag> existingTags = account.getTagList();
+                StringBuilder tmp = new StringBuilder();
+                for (Tag cur : existingTags) {
+                    tmp.append(cur.getTagDefinitionName());
+                    tmp.append(",");
+                }
+                inputTagList = tmp.toString();
+            }
+
+            List<TagDefinition> input = tagHelper.getTagDifinitionFromTagList(tagList);   
+            for (TagDefinition cur : input) {
+                account.removeTag(cur);
+            }
+
+            return Response.status(Status.OK).build();
+        } catch (AccountApiException e) {
+            return Response.status(Status.NO_CONTENT).build();
+        } catch (IllegalArgumentException e) {
+            return Response.status(Status.BAD_REQUEST).entity(e.getMessage()).build();
+        } catch (NullPointerException e) {
+            return Response.status(Status.BAD_REQUEST).entity(e.getMessage()).build();
+        } catch (TagDefinitionApiException e) {
+            return Response.status(Status.BAD_REQUEST).entity(e.getMessage()).build();
+        }
+    }
+    
+    /************************   CUSTOM FIELDS   ******************************/
+    
+    @GET
+    @Path(BaseJaxrsResource.CUSTOM_FIELDS + "/{accountId:" + UUID_PATTERN + "}")
+    @Produces(APPLICATION_JSON)
+    public Response getAccountCustomFields(@PathParam("accountId") String accountId) {
+        try {
+            Account account = accountApi.getAccountById(UUID.fromString(accountId));
+            List<CustomField> fields = account.getFieldList();
+            List<CustomFieldJson> result = new LinkedList<CustomFieldJson>();
+            for (CustomField cur : fields) {
+                result.add(new CustomFieldJson(cur));
+            }
+            return Response.status(Status.OK).entity(result).build();
+        } catch (AccountApiException e) {
+            return Response.status(Status.NO_CONTENT).build();
+        }
+    }
+    
+    
+    @POST
+    @Path(BaseJaxrsResource.CUSTOM_FIELDS + "/{accountId:" + UUID_PATTERN + "}")    
+    @Consumes(APPLICATION_JSON)
+    @Produces(APPLICATION_JSON)
+    public Response createCustomField(@PathParam("accountId") final String accountId,
+            List<CustomFieldJson> customFields,
+            @HeaderParam(HDR_CREATED_BY) final String createdBy,
+            @HeaderParam(HDR_REASON) final String reason,
+            @HeaderParam(HDR_COMMENT) final String comment) {
+
+        try {
+            
+            Account account = accountApi.getAccountById(UUID.fromString(accountId));
+            LinkedList<CustomField> input = new LinkedList<CustomField>();
+            for (CustomFieldJson cur : customFields) {
+                input.add(new StringCustomField(cur.getName(), cur.getValue()));
+            }
+            account.saveFields(input, context.createContext(createdBy, reason, comment));
+            Response response = uriBuilder.buildResponse(AccountResource.class, "getAccountCustomFields", account.getId());            
+            return response;
+        } catch (AccountApiException e) {
+            return Response.status(Status.NO_CONTENT).build();
+        } catch (IllegalArgumentException e) {
+            return Response.status(Status.BAD_REQUEST).entity(e.getMessage()).build();
+        } catch (NullPointerException e) {
+            return Response.status(Status.BAD_REQUEST).entity(e.getMessage()).build();
+        }
+    }
+    
+    @DELETE
+    @Path(BaseJaxrsResource.CUSTOM_FIELDS +  "/{accountId:" + UUID_PATTERN + "}")    
+    @Consumes(APPLICATION_JSON)
+    @Produces(APPLICATION_JSON)
+    public Response deleteCustomFields(@PathParam("accountId") final String accountId,
+            @QueryParam(QUERY_CUSTOM_FIELDS) final String cutomFieldList,
+            @HeaderParam(HDR_CREATED_BY) final String createdBy,
+            @HeaderParam(HDR_REASON) final String reason,
+            @HeaderParam(HDR_COMMENT) final String comment) {
+
+        try {
+            Account account = accountApi.getAccountById(UUID.fromString(accountId));
+            // STEPH missing API to delete custom fields
+            return Response.status(Status.OK).build();
+        } catch (AccountApiException e) {
+            return Response.status(Status.NO_CONTENT).build();
+        } catch (IllegalArgumentException e) {
+            return Response.status(Status.BAD_REQUEST).entity(e.getMessage()).build();
+        } catch (NullPointerException e) {
+            return Response.status(Status.BAD_REQUEST).entity(e.getMessage()).build();
+        }
+    }
+    
 }
diff --git a/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/BaseJaxrsResource.java b/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/BaseJaxrsResource.java
index 79f4b04..2a9e2ee 100644
--- a/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/BaseJaxrsResource.java
+++ b/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/BaseJaxrsResource.java
@@ -17,14 +17,25 @@ package com.ning.billing.jaxrs.resources;
 
 public interface BaseJaxrsResource {
 	
-	public static final String API_PREFIX = "";
-	public static final String API_VERSION = "/1.0";
+    public static final String API_PREFIX = "";
+    public static final String API_VERSION = "/1.0";
+    public static final String API_POSTFIX = "/kb";
+    
+    public static final String PREFIX = API_PREFIX + API_VERSION + API_POSTFIX;
 	
 	public static final String TIMELINE = "timeline";
 	
 	/*
+	 * Metadata Additional headers 
+	 */
+	public static String HDR_CREATED_BY = "X-Killbill-CreatedBy";
+	public static String HDR_REASON = "X-Killbill-Reason";  
+	public static String HDR_COMMENT = "X-Killbill-Comment";   	
+	
+	/*
 	 * Patterns
 	 */
+	public static String STRING_PATTERN = "\\w+";	
 	public static String UUID_PATTERN = "\\w+-\\w+-\\w+-\\w+-\\w+";
 	
 	/*
@@ -33,15 +44,30 @@ public interface BaseJaxrsResource {
 	public static final String QUERY_EXTERNAL_KEY = "external_key";
 	public static final String QUERY_REQUESTED_DT = "requested_date";
 	public static final String QUERY_CALL_COMPLETION = "call_completion";
-	public static final String QUERY_CALL_TIMEOUT = "call_timeout_sec";	
+	public static final String QUERY_CALL_TIMEOUT = "call_timeout_sec";    
+	public static final String QUERY_DRY_RUN = "dry_run";      
+	public static final String QUERY_TARGET_DATE = "target_date";          
+	public static final String QUERY_ACCOUNT_ID = "account_id";           	
 	
-	public static final String ACCOUNTS = "accounts";	
-	public static final String ACCOUNTS_PATH = API_PREFIX + API_VERSION + "/" + ACCOUNTS;
+	public static final String QUERY_TAGS = "tag_list";    
+	public static final String QUERY_CUSTOM_FIELDS = "custom_field_list";    	
+	
+	public static final String ACCOUNTS = "accounts";  
+    public static final String ACCOUNTS_PATH = PREFIX + "/" + ACCOUNTS;
 
 	public static final String BUNDLES = "bundles";		
-	public static final String BUNDLES_PATH = API_PREFIX + API_VERSION + "/" + BUNDLES;
+	public static final String BUNDLES_PATH = PREFIX + "/" + BUNDLES;
+
+    public static final String SUBSCRIPTIONS = "subscriptions";     
+    public static final String SUBSCRIPTIONS_PATH = PREFIX + "/" + SUBSCRIPTIONS;
+
+    public static final String TAG_DEFINITIONS = "tag_definitions";     
+    public static final String TAG_DEFINITIONS_PATH = PREFIX + "/" + TAG_DEFINITIONS;
 
-	public static final String SUBSCRIPTIONS = "subscriptions";		
-	public static final String SUBSCRIPTIONS_PATH = API_PREFIX + API_VERSION + "/" + SUBSCRIPTIONS;
+    public static final String INVOICES = "invoices";     
+    public static final String INVOICES_PATH = PREFIX + "/" + INVOICES;
 
+    
+    public static final String TAGS = "tags";
+    public static final String CUSTOM_FIELDS = "custom_fields";    
 }
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 d3bb1a2..e62d48e 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
@@ -24,6 +24,7 @@ import java.util.UUID;
 
 import javax.ws.rs.Consumes;
 import javax.ws.rs.GET;
+import javax.ws.rs.HeaderParam;
 import javax.ws.rs.POST;
 import javax.ws.rs.Path;
 import javax.ws.rs.PathParam;
@@ -102,14 +103,20 @@ public class BundleResource implements BaseJaxrsResource {
     @POST
     @Consumes(APPLICATION_JSON)
     @Produces(APPLICATION_JSON)
-    public Response createBundle(final BundleJsonNoSubsciptions json) {
+    public Response createBundle(final BundleJsonNoSubsciptions json,
+            @HeaderParam(HDR_CREATED_BY) final String createdBy,
+            @HeaderParam(HDR_REASON) final String reason,
+            @HeaderParam(HDR_COMMENT) final String comment) {
         try {
             UUID accountId = UUID.fromString(json.getAccountId());
-            final SubscriptionBundle bundle = entitlementApi.createBundleForAccount(accountId, json.getExternalKey(), context.createContext());
+            final SubscriptionBundle bundle = entitlementApi.createBundleForAccount(accountId, json.getExternalKey(),
+                    context.createContext(createdBy, reason, comment));
             return uriBuilder.buildResponse(BundleResource.class, "getBundle", bundle.getId());
         } catch (EntitlementUserApiException e) {
             log.info(String.format("Failed to create bundle %s", json), e);
             return Response.status(Status.BAD_REQUEST).build();
+        } catch (IllegalArgumentException e) {
+            return Response.status(Status.BAD_REQUEST).entity(e.getMessage()).build();
         }
     }
 
diff --git a/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/InvoiceResource.java b/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/InvoiceResource.java
index e5a1c97..27b7817 100644
--- a/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/InvoiceResource.java
+++ b/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/InvoiceResource.java
@@ -18,8 +18,14 @@ package com.ning.billing.jaxrs.resources;
 
 import static javax.ws.rs.core.MediaType.APPLICATION_JSON;
 
+import java.util.LinkedList;
+import java.util.List;
+import java.util.UUID;
+
 import javax.ws.rs.Consumes;
+import javax.ws.rs.DefaultValue;
 import javax.ws.rs.GET;
+import javax.ws.rs.HeaderParam;
 import javax.ws.rs.POST;
 import javax.ws.rs.Path;
 import javax.ws.rs.PathParam;
@@ -29,34 +35,109 @@ import javax.ws.rs.QueryParam;
 import javax.ws.rs.core.Response;
 import javax.ws.rs.core.Response.Status;
 
+import org.joda.time.DateTime;
+import org.joda.time.format.DateTimeFormatter;
+import org.joda.time.format.ISODateTimeFormat;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.base.Preconditions;
+import com.google.inject.Inject;
+import com.ning.billing.account.api.AccountApiException;
+import com.ning.billing.account.api.AccountUserApi;
+import com.ning.billing.invoice.api.Invoice;
+import com.ning.billing.invoice.api.InvoiceApiException;
+import com.ning.billing.invoice.api.InvoiceUserApi;
 import com.ning.billing.jaxrs.json.InvoiceJson;
+import com.ning.billing.jaxrs.util.Context;
+import com.ning.billing.jaxrs.util.JaxrsUriBuilder;
 
 
+@Path(BaseJaxrsResource.INVOICES_PATH)
+public class InvoiceResource implements BaseJaxrsResource {
 
-@Path("/1.0/invoice")
-public class InvoiceResource {
 
+    private static final Logger log = LoggerFactory.getLogger(AccountResource.class);
 
+    private final DateTimeFormatter DATE_TIME_FORMATTER = ISODateTimeFormat.dateTime();
+    
+    private final AccountUserApi accountApi;
+    private final InvoiceUserApi invoiceApi;
+    private final Context context;
+    private final JaxrsUriBuilder uriBuilder;
+    
+    @Inject
+    public InvoiceResource(final AccountUserApi accountApi,
+            final InvoiceUserApi invoiceApi,
+            final Context context,
+            final JaxrsUriBuilder uriBuilder) {
+        this.accountApi = accountApi;
+        this.invoiceApi = invoiceApi;
+        this.context = context;
+        this.uriBuilder = uriBuilder;
+    }
+    
     @GET
     @Produces(APPLICATION_JSON)
-    public Response getInvoices(@QueryParam("accountId") String accountId) {
-        return Response.status(Status.INTERNAL_SERVER_ERROR).build();
+    public Response getInvoices(@QueryParam(QUERY_ACCOUNT_ID) final String accountId) {
+        try {
+            
+            Preconditions.checkNotNull(accountId, "% query parameter must be specified", QUERY_ACCOUNT_ID);
+            accountApi.getAccountById(UUID.fromString(accountId));
+            List<Invoice> invoices = invoiceApi.getInvoicesByAccount(UUID.fromString(accountId));
+            List<InvoiceJson> result = new LinkedList<InvoiceJson>();
+            for (Invoice cur : invoices) {
+                result.add(new InvoiceJson(cur));
+            }
+            return Response.status(Status.OK).entity(result).build();
+        } catch (AccountApiException e) {
+            return Response.status(Status.NO_CONTENT).build();            
+        } catch (NullPointerException e) {
+            return Response.status(Status.BAD_REQUEST).build();            
+        }
     }
 
     @GET
     @Path("/{invoiceId:\\w+-\\w+-\\w+-\\w+-\\w+}")
     @Produces(APPLICATION_JSON)
-    public Response getInvoice(@PathParam("invoiceId") String accountId) {
-        return Response.status(Status.INTERNAL_SERVER_ERROR).build();
+    public Response getInvoice(@PathParam("invoiceId") String invoiceId) {
+        Invoice invoice = invoiceApi.getInvoice(UUID.fromString(invoiceId));
+        InvoiceJson json = new InvoiceJson(invoice);
+        return Response.status(Status.OK).entity(json).build();
     }
 
     @POST
-    @Path("/{accountId:\\w+-\\w+-\\w+-\\w+-\\w+}")
     @Consumes(APPLICATION_JSON)
     @Produces(APPLICATION_JSON)
-    public Response createFutureInvoice(InvoiceJson invoice,
-            @PathParam("accountId") String accountId,
-            @QueryParam("targetDate") String targetDate) {
-        return Response.status(Status.INTERNAL_SERVER_ERROR).build();
+    public Response createFutureInvoice(final InvoiceJson invoice,
+            @QueryParam(QUERY_ACCOUNT_ID) final String accountId,
+            @QueryParam(QUERY_TARGET_DATE) final String targetDate,
+            @QueryParam(QUERY_DRY_RUN) @DefaultValue("false") final Boolean dryRun,
+            @HeaderParam(HDR_CREATED_BY) final String createdBy,
+            @HeaderParam(HDR_REASON) final String reason,
+            @HeaderParam(HDR_COMMENT) final String comment) {
+
+        try {
+            
+            Preconditions.checkNotNull(accountId, "% needs to be specified", QUERY_ACCOUNT_ID);
+            Preconditions.checkNotNull(targetDate, "% needs to be specified", QUERY_TARGET_DATE);
+            
+            DateTime inputDate = (targetDate != null) ? DATE_TIME_FORMATTER.parseDateTime(targetDate) : null;        
+            
+            accountApi.getAccountById(UUID.fromString(accountId));
+            Invoice generatedInvoice = invoiceApi.triggerInvoiceGeneration(UUID.fromString(accountId), inputDate, dryRun.booleanValue(),
+                    context.createContext(createdBy, reason, comment));
+            if (dryRun) {
+                return Response.status(Status.OK).entity(new InvoiceJson(generatedInvoice)).build();
+            } else {
+               return uriBuilder.buildResponse(InvoiceResource.class, "getInvoice", generatedInvoice.getId());
+            }
+        } catch (AccountApiException e) {
+            return Response.status(Status.BAD_REQUEST).entity(e.getMessage()).build();  
+        } catch (InvoiceApiException e) {
+            return Response.status(Status.BAD_REQUEST).entity(e.getMessage()).build();  
+        } catch (NullPointerException e) {
+            return Response.status(Status.BAD_REQUEST).entity(e.getMessage()).build();            
+        }
     }
 }
diff --git a/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/SubscriptionResource.java b/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/SubscriptionResource.java
index 09db7f9..67c5b5c 100644
--- a/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/SubscriptionResource.java
+++ b/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/SubscriptionResource.java
@@ -26,6 +26,7 @@ import javax.ws.rs.Consumes;
 import javax.ws.rs.DELETE;
 import javax.ws.rs.DefaultValue;
 import javax.ws.rs.GET;
+import javax.ws.rs.HeaderParam;
 import javax.ws.rs.POST;
 import javax.ws.rs.PUT;
 import javax.ws.rs.Path;
@@ -74,8 +75,6 @@ public class SubscriptionResource implements BaseJaxrsResource {
     private final JaxrsUriBuilder uriBuilder;	
     private final KillbillEventHandler killbillHandler;
     
-
-    
     @Inject
     public SubscriptionResource(final JaxrsUriBuilder uriBuilder, final EntitlementUserApi entitlementApi,
             final Clock clock, final Context context, final KillbillEventHandler killbillHandler) {
@@ -93,7 +92,6 @@ public class SubscriptionResource implements BaseJaxrsResource {
         try {
             UUID uuid = UUID.fromString(subscriptionId);
             Subscription subscription = entitlementApi.getSubscriptionFromId(uuid);
-
             SubscriptionJsonNoEvents json = new SubscriptionJsonNoEvents(subscription);
             return Response.status(Status.OK).entity(json).build();
         } catch (EntitlementUserApiException e) {
@@ -102,50 +100,20 @@ public class SubscriptionResource implements BaseJaxrsResource {
             } else {
                 throw e;
             }
-
         }
     }
-    
-    /*
-    @GET
-    @Path("/{subscriptionId:" + UUID_PATTERN + "}")
-    @Produces(APPLICATION_JSON)
-    public StreamingOutput getSubscription(@PathParam("subscriptionId") final String subscriptionId) {
-
-        UUID uuid = UUID.fromString(subscriptionId);
-        final Subscription subscription = entitlementApi.getSubscriptionFromId(uuid);
-        if (subscription == null) {
-            throw new WebApplicationException(Response.Status.NO_CONTENT);
-        }
-        final SubscriptionJson json = new SubscriptionJson(subscription, null, null, null);
-        return new StreamingOutput() {
-
-            final SubscriptionJson json = new SubscriptionJson(subscription, null, null, null);
-            
-            @Override
-            public void write(OutputStream output) throws IOException,
-                    WebApplicationException {
-                
-                final ObjectWriter objWriter = objectMapper.writerWithView(BundleTimelineViews.Base.class);
-                
-                Writer writer = new StringWriter();
-                objWriter.writeValue(writer, json);
-                String baseJson = writer.toString();
-                output.write(baseJson.getBytes());
-                output.flush();
-            }
-        };
-    }
-    */
-
+  
 
     @POST
     @Consumes(APPLICATION_JSON)
     @Produces(APPLICATION_JSON)
     public Response createSubscription(final SubscriptionJsonNoEvents subscription,
-            final @QueryParam(QUERY_REQUESTED_DT) String requestedDate,
-            final @QueryParam(QUERY_CALL_COMPLETION) @DefaultValue("false") Boolean callCompletion,
-            final @QueryParam(QUERY_CALL_TIMEOUT) @DefaultValue("3") long timeoutSec) {
+            @QueryParam(QUERY_REQUESTED_DT) final String requestedDate,
+            @QueryParam(QUERY_CALL_COMPLETION) @DefaultValue("false") final Boolean callCompletion,
+            @QueryParam(QUERY_CALL_TIMEOUT) @DefaultValue("3") final long timeoutSec,
+            @HeaderParam(HDR_CREATED_BY) final String createdBy,
+            @HeaderParam(HDR_REASON) final String reason,
+            @HeaderParam(HDR_COMMENT) final String comment) {
 
 
         SubscriptionCallCompletionCallback<Subscription> callback = new SubscriptionCallCompletionCallback<Subscription>() {
@@ -170,7 +138,7 @@ public class SubscriptionResource implements BaseJaxrsResource {
             }
         };
         SubscriptionCallCompletion<Subscription> callCompletionCreation = new SubscriptionCallCompletion<Subscription>();
-        return callCompletionCreation.withSynchronization(callback, timeoutSec, callCompletion);
+        return callCompletionCreation.withSynchronization(callback, timeoutSec, callCompletion, createdBy, reason, comment);
     }
 
     @PUT
@@ -178,10 +146,13 @@ public class SubscriptionResource implements BaseJaxrsResource {
     @Consumes(APPLICATION_JSON)
     @Path("/{subscriptionId:" + UUID_PATTERN + "}")
     public Response changeSubscriptionPlan(final SubscriptionJsonNoEvents subscription,
-            final @PathParam("subscriptionId") String subscriptionId,
-            final @QueryParam(QUERY_REQUESTED_DT) String requestedDate,
-            final @QueryParam(QUERY_CALL_COMPLETION) @DefaultValue("false") Boolean callCompletion,
-            final @QueryParam(QUERY_CALL_TIMEOUT) @DefaultValue("3") long timeoutSec) {
+            @PathParam("subscriptionId") final String subscriptionId,
+            @QueryParam(QUERY_REQUESTED_DT) final String requestedDate,
+            @QueryParam(QUERY_CALL_COMPLETION) @DefaultValue("false") final Boolean callCompletion,
+            @QueryParam(QUERY_CALL_TIMEOUT) @DefaultValue("3") final long timeoutSec,
+            @HeaderParam(HDR_CREATED_BY) final String createdBy,
+            @HeaderParam(HDR_REASON) final String reason,
+            @HeaderParam(HDR_COMMENT) final String comment) {
 
         SubscriptionCallCompletionCallback<Response> callback = new SubscriptionCallCompletionCallback<Response>() {
 
@@ -212,34 +183,35 @@ public class SubscriptionResource implements BaseJaxrsResource {
                     return operationResponse;
                 }
                 try {
-                return getSubscription(subscriptionId);
+                    return getSubscription(subscriptionId);
                 } catch (EntitlementUserApiException e) {
                     if (e.getCode() == ErrorCode.ENT_GET_INVALID_BUNDLE_ID.getCode()) {
                         return Response.status(Status.NO_CONTENT).build();
                     } else {
                         return Response.status(Status.INTERNAL_SERVER_ERROR).build();
                     }
-
                 }
             }
         };
         SubscriptionCallCompletion<Response> callCompletionCreation = new SubscriptionCallCompletion<Response>();
-        return callCompletionCreation.withSynchronization(callback, timeoutSec, callCompletion);
+        return callCompletionCreation.withSynchronization(callback, timeoutSec, callCompletion, createdBy, reason, comment);
     }
 
     @PUT
     @Path("/{subscriptionId:" + UUID_PATTERN + "}/uncancel")
     @Produces(APPLICATION_JSON)
-    public Response uncancelSubscriptionPlan(@PathParam("subscriptionId") String subscriptionId) {
+    public Response uncancelSubscriptionPlan(@PathParam("subscriptionId") final String subscriptionId,
+            @HeaderParam(HDR_CREATED_BY) final String createdBy,
+            @HeaderParam(HDR_REASON) final String reason,
+            @HeaderParam(HDR_COMMENT) final String comment) {
         try {
             UUID uuid = UUID.fromString(subscriptionId);
             Subscription current = entitlementApi.getSubscriptionFromId(uuid);
         
-            current.uncancel(context.createContext());
+            current.uncancel(context.createContext(createdBy, reason, comment));
             return Response.status(Status.OK).build();
         } catch (EntitlementUserApiException e) {
             if(e.getCode() == ErrorCode.ENT_INVALID_SUBSCRIPTION_ID.getCode()) {
-                log.info(String.format("Failed to find subscription %s", subscriptionId), e);
                 return Response.status(Status.NO_CONTENT).build();
             } else {
                 log.info(String.format("Failed to uncancel plan for subscription %s", subscriptionId), e);
@@ -252,9 +224,12 @@ public class SubscriptionResource implements BaseJaxrsResource {
     @Path("/{subscriptionId:" + UUID_PATTERN + "}")
     @Produces(APPLICATION_JSON)
     public Response cancelSubscriptionPlan(final @PathParam("subscriptionId") String subscriptionId,
-            final @QueryParam(QUERY_REQUESTED_DT) String requestedDate,
-            final @QueryParam(QUERY_CALL_COMPLETION) @DefaultValue("false") Boolean callCompletion,
-            final @QueryParam(QUERY_CALL_TIMEOUT) @DefaultValue("3") long timeoutSec) {
+            @QueryParam(QUERY_REQUESTED_DT) final String requestedDate,
+            @QueryParam(QUERY_CALL_COMPLETION) @DefaultValue("false") final Boolean callCompletion,
+            @QueryParam(QUERY_CALL_TIMEOUT) @DefaultValue("3") final long timeoutSec,
+            @HeaderParam(HDR_CREATED_BY) final String createdBy,
+            @HeaderParam(HDR_REASON) final String reason,
+            @HeaderParam(HDR_COMMENT) final String comment) {
 
         SubscriptionCallCompletionCallback<Response> callback = new SubscriptionCallCompletionCallback<Response>() {
 
@@ -274,7 +249,6 @@ public class SubscriptionResource implements BaseJaxrsResource {
                     return Response.status(Status.OK).build();
                 } catch (EntitlementUserApiException e) {
                     if(e.getCode() == ErrorCode.ENT_INVALID_SUBSCRIPTION_ID.getCode()) {
-                        log.info(String.format("Failed to find subscription %s", subscriptionId), e);
                         return Response.status(Status.NO_CONTENT).build();
                     } else {
                         throw e;
@@ -291,7 +265,7 @@ public class SubscriptionResource implements BaseJaxrsResource {
             }
         };
         SubscriptionCallCompletion<Response> callCompletionCreation = new SubscriptionCallCompletion<Response>();
-        return callCompletionCreation.withSynchronization(callback, timeoutSec, callCompletion);
+        return callCompletionCreation.withSynchronization(callback, timeoutSec, callCompletion, createdBy, reason, comment);
     }
 
     private final static class CompletionUserRequestSubscription extends CompletionUserRequestBase {
@@ -336,9 +310,14 @@ public class SubscriptionResource implements BaseJaxrsResource {
 
     private class SubscriptionCallCompletion<T> {
 
-        public Response withSynchronization(final SubscriptionCallCompletionCallback<T> callback, final long timeoutSec, final boolean callCompletion) {
+        public Response withSynchronization(final SubscriptionCallCompletionCallback<T> callback,
+                final long timeoutSec,
+                final boolean callCompletion,
+                final String createdBy,
+                final String reason,
+                final String comment) {
 
-            CallContext ctx = context.createContext();
+            CallContext ctx = context.createContext(createdBy, reason, comment);
             CompletionUserRequestSubscription waiter = callCompletion ? new CompletionUserRequestSubscription(ctx.getUserToken()) : null; 
             try {
                 if (waiter != null) {
@@ -351,13 +330,10 @@ public class SubscriptionResource implements BaseJaxrsResource {
                 return callback.doResponseOk(operationValue);
             } catch (EntitlementUserApiException e) {
                 log.info(String.format("Failed to complete operation"), e);
-                //throw new WebApplicationException(Response.Status.BAD_REQUEST);
                 return Response.status(Status.BAD_REQUEST).build();
             } catch (InterruptedException e) {
-                //throw new WebApplicationException(Response.Status.INTERNAL_SERVER_ERROR);                
                 return Response.status(Status.INTERNAL_SERVER_ERROR).build();
             } catch (TimeoutException e) {
-                //throw new WebApplicationException(Response.Status.fromStatusCode(408));                
                 return Response.status(Status.fromStatusCode(408)).build();   
             } finally {
                 if (waiter != null) {
diff --git a/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/TagResource.java b/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/TagResource.java
index cc19095..3111240 100644
--- a/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/TagResource.java
+++ b/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/TagResource.java
@@ -16,6 +16,104 @@
 
 package com.ning.billing.jaxrs.resources;
 
-public class TagResource {
+import static javax.ws.rs.core.MediaType.APPLICATION_JSON;
 
+import java.util.LinkedList;
+import java.util.List;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.GET;
+import javax.ws.rs.HeaderParam;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.Response.Status;
+
+import com.google.inject.Inject;
+import com.google.inject.Singleton;
+import com.ning.billing.jaxrs.json.TagDefinitionJson;
+import com.ning.billing.jaxrs.util.Context;
+import com.ning.billing.jaxrs.util.JaxrsUriBuilder;
+import com.ning.billing.util.api.TagDefinitionApiException;
+import com.ning.billing.util.api.TagUserApi;
+import com.ning.billing.util.tag.TagDefinition;
+
+@Singleton
+@Path(BaseJaxrsResource.TAG_DEFINITIONS_PATH)
+public class TagResource implements BaseJaxrsResource {
+    
+    private final TagUserApi tagUserApi;
+    private final Context context;
+    private final JaxrsUriBuilder uriBuilder;
+    
+    @Inject
+    public TagResource(TagUserApi tagUserApi, final JaxrsUriBuilder uriBuilder, final Context context) {
+        this.tagUserApi = tagUserApi;
+        this.context = context;
+        this.uriBuilder = uriBuilder;
+    }
+    
+    @GET
+    @Produces(APPLICATION_JSON)
+    public Response getTagDefinitions() {
+        
+        List<TagDefinitionJson> result = new LinkedList<TagDefinitionJson>();
+        List<TagDefinition> tagDefinitions = tagUserApi.getTagDefinitions();
+        for (TagDefinition cur : tagDefinitions) {
+            result.add(new TagDefinitionJson(cur.getName(), cur.getDescription()));
+        }
+        return Response.status(Status.OK).entity(result).build();
+    }
+    
+    @GET
+    @Path("/{tagDefinitionName:" + STRING_PATTERN + "}")
+    @Produces(APPLICATION_JSON)
+    public Response getTagDefinition(@PathParam("tagDefinitionName") final String tagDefName) {
+        try {
+            TagDefinition tagDef = tagUserApi.getTagDefinition(tagDefName);
+            TagDefinitionJson json = new TagDefinitionJson(tagDef.getName(), tagDef.getDescription());
+            return Response.status(Status.OK).entity(json).build();
+        } catch (TagDefinitionApiException e) {
+            return Response.status(Status.NO_CONTENT).build(); 
+        }
+    }
+
+
+
+    @POST
+    @Consumes(APPLICATION_JSON)
+    @Produces(APPLICATION_JSON)
+    public Response createTagDefinition(final TagDefinitionJson json,
+            @HeaderParam(HDR_CREATED_BY) final String createdBy,
+            @HeaderParam(HDR_REASON) final String reason,
+            @HeaderParam(HDR_COMMENT) final String comment) {
+        try {
+            TagDefinition createdTagDef =  tagUserApi.create(json.getName(), json.getDescription(), context.createContext(createdBy, reason, comment));
+            return uriBuilder.buildResponse(TagResource.class, "getTagDefinition", createdTagDef.getName());
+        } catch (TagDefinitionApiException e) {
+            return Response.status(Status.NO_CONTENT).build(); 
+        } catch (IllegalArgumentException e) {
+            return Response.status(Status.BAD_REQUEST).entity(e.getMessage()).build();
+        }
+    }
+    
+    @DELETE
+    @Path("/{tagDefinitionName:" + STRING_PATTERN + "}")
+    @Produces(APPLICATION_JSON)
+    public Response deleteTagDefinition(@PathParam("tagDefinitionName") String tagDefName,
+            @HeaderParam(HDR_CREATED_BY) final String createdBy,
+            @HeaderParam(HDR_REASON) final String reason,
+            @HeaderParam(HDR_COMMENT) final String comment) {
+        try {
+            tagUserApi.deleteTagDefinition(tagDefName, context.createContext(createdBy, reason, comment));
+            return Response.status(Status.NO_CONTENT).build();
+        } catch (TagDefinitionApiException e) {
+            return Response.status(Status.NO_CONTENT).build(); 
+        } catch (IllegalArgumentException e) {
+            return Response.status(Status.BAD_REQUEST).entity(e.getMessage()).build();
+        }
+    }
 }
diff --git a/jaxrs/src/main/java/com/ning/billing/jaxrs/util/Context.java b/jaxrs/src/main/java/com/ning/billing/jaxrs/util/Context.java
index 004a468..729f166 100644
--- a/jaxrs/src/main/java/com/ning/billing/jaxrs/util/Context.java
+++ b/jaxrs/src/main/java/com/ning/billing/jaxrs/util/Context.java
@@ -17,7 +17,9 @@ package com.ning.billing.jaxrs.util;
 
 import java.util.UUID;
 
+import com.google.common.base.Preconditions;
 import com.google.inject.Inject;
+import com.ning.billing.jaxrs.resources.BaseJaxrsResource;
 import com.ning.billing.util.callcontext.CallContext;
 import com.ning.billing.util.callcontext.CallContextFactory;
 import com.ning.billing.util.callcontext.CallOrigin;
@@ -37,8 +39,13 @@ public class Context {
         this.contextFactory = factory;
     }
 
-    // Simplistic until we decide how to populate that
-    public CallContext createContext() {
-        return contextFactory.createCallContext("Unknown", origin, userType, UUID.randomUUID());
+    public CallContext createContext(final String createdBy, final String reason, final String comment)
+    throws IllegalArgumentException {
+        try {
+            Preconditions.checkNotNull(createdBy, String.format("Header %s needs to be set", BaseJaxrsResource.HDR_CREATED_BY));
+            return contextFactory.createCallContext(createdBy, origin, userType, UUID.randomUUID());
+        } catch (NullPointerException e) {
+            throw new IllegalArgumentException(e.getMessage());
+        }
     }
 }
diff --git a/jaxrs/src/main/java/com/ning/billing/jaxrs/util/JaxrsUriBuilder.java b/jaxrs/src/main/java/com/ning/billing/jaxrs/util/JaxrsUriBuilder.java
index a38a9fe..b351b4c 100644
--- a/jaxrs/src/main/java/com/ning/billing/jaxrs/util/JaxrsUriBuilder.java
+++ b/jaxrs/src/main/java/com/ning/billing/jaxrs/util/JaxrsUriBuilder.java
@@ -26,7 +26,7 @@ import com.ning.billing.jaxrs.resources.BaseJaxrsResource;
 public class JaxrsUriBuilder {
 
 	
-	public Response buildResponse(final Class<? extends BaseJaxrsResource> theClass, final String getMethodName, final UUID objectId) {
+	public Response buildResponse(final Class<? extends BaseJaxrsResource> theClass, final String getMethodName, final Object objectId) {
 		URI uri = UriBuilder.fromPath(objectId.toString()).build();
 		Response.ResponseBuilder ri = Response.created(uri);
 		return ri.entity(new Object() {
diff --git a/jaxrs/src/main/java/com/ning/billing/jaxrs/util/TagHelper.java b/jaxrs/src/main/java/com/ning/billing/jaxrs/util/TagHelper.java
new file mode 100644
index 0000000..e965deb
--- /dev/null
+++ b/jaxrs/src/main/java/com/ning/billing/jaxrs/util/TagHelper.java
@@ -0,0 +1,49 @@
+/* 
+ * 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.jaxrs.util;
+
+import java.util.LinkedList;
+import java.util.List;
+
+import com.google.inject.Inject;
+import com.ning.billing.ErrorCode;
+import com.ning.billing.util.api.TagDefinitionApiException;
+import com.ning.billing.util.api.TagUserApi;
+import com.ning.billing.util.tag.TagDefinition;
+
+public class TagHelper {
+
+    private final TagUserApi tagUserApi;
+    
+    @Inject
+    public TagHelper(final TagUserApi tagUserApi) {
+        this.tagUserApi = tagUserApi;
+    }
+    
+    public List<TagDefinition> getTagDifinitionFromTagList(final String tagList) throws TagDefinitionApiException {
+        List<TagDefinition> result = new LinkedList<TagDefinition>();
+        String [] tagParts = tagList.split(",\\s*");
+        for (String cur : tagParts) {
+            TagDefinition curDef = tagUserApi.getTagDefinition(cur);
+            // Yack should throw excption
+            if (curDef == null) {
+                throw new TagDefinitionApiException(ErrorCode.TAG_DEFINITION_DOES_NOT_EXIST, cur);
+            }
+            result.add(curDef);
+        }
+        return result;
+    }
+}
diff --git a/jaxrs/src/main/resources/.dont-let-git-remove-this-directory b/jaxrs/src/main/resources/.dont-let-git-remove-this-directory
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/jaxrs/src/main/resources/.dont-let-git-remove-this-directory

junction/pom.xml 14(+2 -12)

diff --git a/junction/pom.xml b/junction/pom.xml
index a33d843..0375b37 100644
--- a/junction/pom.xml
+++ b/junction/pom.xml
@@ -55,6 +55,7 @@
             <artifactId>commons-io</artifactId>
             <scope>test</scope>
         </dependency>
+        <!-- TEST SCOPE -->
         <dependency>
             <groupId>com.ning.billing</groupId>
             <artifactId>killbill-util</artifactId>
@@ -72,18 +73,7 @@
             <artifactId>killbill-catalog</artifactId>
             <scope>test</scope>
         </dependency>
-        <dependency>
-            <groupId>com.ning.billing</groupId>
-            <artifactId>killbill-entitlement</artifactId>
-            <type>test-jar</type>
-            <scope>test</scope>
-        </dependency>
-        <dependency>
-            <groupId>com.ning.billing</groupId>
-            <artifactId>killbill-entitlement</artifactId>
-            <scope>test</scope>
-        </dependency>
-        <dependency>
+         <dependency>
             <groupId>mysql</groupId>
             <artifactId>mysql-connector-java</artifactId>
             <scope>runtime</scope>
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 3d340da..9452146 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
@@ -29,6 +29,7 @@ import com.ning.billing.entitlement.api.user.EntitlementUserApi;
 import com.ning.billing.entitlement.api.user.EntitlementUserApiException;
 import com.ning.billing.entitlement.api.user.Subscription;
 import com.ning.billing.entitlement.api.user.SubscriptionBundle;
+import com.ning.billing.entitlement.api.user.SubscriptionStatusDryRun;
 import com.ning.billing.junction.api.Blockable;
 import com.ning.billing.junction.api.BlockingApi;
 import com.ning.billing.junction.api.BlockingApiException;
@@ -40,7 +41,7 @@ public class BlockingEntitlementUserApi implements EntitlementUserApi {
     private final EntitlementUserApi entitlementUserApi;
     private final BlockingApi blockingApi;
     private final BlockingChecker checker;
-    
+
     @Inject
     public BlockingEntitlementUserApi(@RealImplementation EntitlementUserApi userApi, BlockingApi blockingApi, BlockingChecker checker) {
         this.entitlementUserApi = userApi;
@@ -48,32 +49,25 @@ public class BlockingEntitlementUserApi implements EntitlementUserApi {
         this.checker = checker;
     }
 
+    @Override
     public SubscriptionBundle getBundleFromId(UUID id) throws EntitlementUserApiException {
         SubscriptionBundle bundle = entitlementUserApi.getBundleFromId(id);
-        if(bundle == null) {
-            throw new EntitlementUserApiException(ErrorCode.ENT_GET_INVALID_BUNDLE_ID, id);
-        }
         return new BlockingSubscriptionBundle(bundle, blockingApi);
     }
 
+    @Override
     public Subscription getSubscriptionFromId(UUID id) throws EntitlementUserApiException {
         Subscription subscription = entitlementUserApi.getSubscriptionFromId(id);
-        if(subscription == null) {
-            throw new EntitlementUserApiException(ErrorCode.ENT_INVALID_SUBSCRIPTION_ID, id);
-        }
         return new BlockingSubscription(subscription, blockingApi, checker);
     }
 
-    
+    @Override
     public SubscriptionBundle getBundleForKey(String bundleKey) throws EntitlementUserApiException {
         SubscriptionBundle bundle = entitlementUserApi.getBundleForKey(bundleKey);
-        if(bundle == null) {
-            throw new EntitlementUserApiException(ErrorCode.ENT_GET_INVALID_BUNDLE_KEY, bundleKey);
-        }
-
         return new BlockingSubscriptionBundle(bundle, blockingApi);
     }
 
+    @Override
     public List<SubscriptionBundle> getBundlesForAccount(UUID accountId) {
         List<SubscriptionBundle> result = new ArrayList<SubscriptionBundle>();
         List<SubscriptionBundle> bundles = entitlementUserApi.getBundlesForAccount(accountId);
@@ -83,6 +77,7 @@ public class BlockingEntitlementUserApi implements EntitlementUserApi {
         return result;
     }
 
+    @Override
     public List<Subscription> getSubscriptionsForBundle(UUID bundleId) {
         List<Subscription> result = new ArrayList<Subscription>();
         List<Subscription> subscriptions = entitlementUserApi.getSubscriptionsForBundle(bundleId);
@@ -92,6 +87,7 @@ public class BlockingEntitlementUserApi implements EntitlementUserApi {
         return result;
     }
 
+    @Override
     public List<Subscription> getSubscriptionsForKey(String bundleKey) {
         List<Subscription> result = new ArrayList<Subscription>();
         List<Subscription> subscriptions = entitlementUserApi.getSubscriptionsForKey(bundleKey);
@@ -101,20 +97,30 @@ public class BlockingEntitlementUserApi implements EntitlementUserApi {
         return result;
     }
 
-    public Subscription getBaseSubscription(UUID bundleId) {
+    @Override
+    public List<SubscriptionStatusDryRun> getDryRunChangePlanStatus(
+            UUID subscriptionId, String productName, DateTime requestedDate)
+            throws EntitlementUserApiException {
+        return entitlementUserApi.getDryRunChangePlanStatus(subscriptionId, productName, requestedDate);
+    }
+
+    @Override
+    public Subscription getBaseSubscription(UUID bundleId) throws EntitlementUserApiException {
         return new BlockingSubscription(entitlementUserApi.getBaseSubscription(bundleId), blockingApi, checker);
     }
 
+    @Override
     public SubscriptionBundle createBundleForAccount(UUID accountId, String bundleKey, CallContext context)
-            throws EntitlementUserApiException {
+    throws EntitlementUserApiException {
         try {
-           checker.checkBlockedChange(accountId, Blockable.Type.ACCOUNT);
-           return new BlockingSubscriptionBundle(entitlementUserApi.createBundleForAccount(accountId, bundleKey, context), blockingApi);
+            checker.checkBlockedChange(accountId, Blockable.Type.ACCOUNT);
+            return new BlockingSubscriptionBundle(entitlementUserApi.createBundleForAccount(accountId, bundleKey, context), blockingApi);
         }catch (BlockingApiException e) {
             throw new EntitlementUserApiException(e, e.getCode(), e.getMessage());
         }
-   }
+    }
 
+    @Override
     public Subscription createSubscription(UUID bundleId, PlanPhaseSpecifier spec, DateTime requestedDate,
             CallContext context) throws EntitlementUserApiException {
         try {
@@ -125,8 +131,8 @@ public class BlockingEntitlementUserApi implements EntitlementUserApi {
         }
     }
 
+    @Override
     public DateTime getNextBillingDate(UUID account) {
         return entitlementUserApi.getNextBillingDate(account);
     }
-
 }
diff --git a/junction/src/main/java/com/ning/billing/junction/plumbing/billing/BillCycleDayCalculator.java b/junction/src/main/java/com/ning/billing/junction/plumbing/billing/BillCycleDayCalculator.java
index d2c06b9..b1c5558 100644
--- a/junction/src/main/java/com/ning/billing/junction/plumbing/billing/BillCycleDayCalculator.java
+++ b/junction/src/main/java/com/ning/billing/junction/plumbing/billing/BillCycleDayCalculator.java
@@ -34,6 +34,7 @@ import com.ning.billing.catalog.api.PlanPhaseSpecifier;
 import com.ning.billing.catalog.api.Product;
 import com.ning.billing.entitlement.api.SubscriptionTransitionType;
 import com.ning.billing.entitlement.api.user.EntitlementUserApi;
+import com.ning.billing.entitlement.api.user.EntitlementUserApiException;
 import com.ning.billing.entitlement.api.user.Subscription;
 import com.ning.billing.entitlement.api.user.SubscriptionBundle;
 import com.ning.billing.entitlement.api.user.SubscriptionEvent;
@@ -51,7 +52,8 @@ public class BillCycleDayCalculator {
 		this.entitlementApi = entitlementApi;
 	}
 
-	protected int calculateBcd(SubscriptionBundle bundle, Subscription subscription, final SubscriptionEvent transition, final Account account) throws CatalogApiException, AccountApiException {
+	protected int calculateBcd(SubscriptionBundle bundle, Subscription subscription, final SubscriptionEvent transition, final Account account)
+	throws CatalogApiException, AccountApiException, EntitlementUserApiException {
 
 	    Catalog catalog = catalogService.getFullCatalog();
 		
@@ -84,7 +86,8 @@ public class BillCycleDayCalculator {
 		    break;
 		case BUNDLE :
 		    Subscription baseSub = entitlementApi.getBaseSubscription(bundle.getId());
-		    result = calculateBcdFromSubscription(baseSub, plan, account);
+		    Plan basePlan = baseSub.getCurrentPlan();
+		    result = calculateBcdFromSubscription(baseSub, basePlan, account);
 		    break;
 		case SUBSCRIPTION :
 		    result = calculateBcdFromSubscription(subscription, plan, account);
diff --git a/junction/src/main/java/com/ning/billing/junction/plumbing/billing/DefaultBillingApi.java b/junction/src/main/java/com/ning/billing/junction/plumbing/billing/DefaultBillingApi.java
index e8945c5..8f88049 100644
--- a/junction/src/main/java/com/ning/billing/junction/plumbing/billing/DefaultBillingApi.java
+++ b/junction/src/main/java/com/ning/billing/junction/plumbing/billing/DefaultBillingApi.java
@@ -22,11 +22,11 @@ import java.util.TreeSet;
 import java.util.UUID;
 
 import org.joda.time.DateTime;
-import org.skife.jdbi.v2.sqlobject.mixins.Transmogrifier;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import com.google.inject.Inject;
+import com.ning.billing.ErrorCode;
 import com.ning.billing.account.api.Account;
 import com.ning.billing.account.api.AccountApiException;
 import com.ning.billing.account.api.AccountUserApi;
@@ -35,6 +35,7 @@ import com.ning.billing.catalog.api.CatalogApiException;
 import com.ning.billing.catalog.api.CatalogService;
 import com.ning.billing.entitlement.api.billing.BillingEvent;
 import com.ning.billing.entitlement.api.billing.ChargeThruApi;
+import com.ning.billing.entitlement.api.billing.EntitlementBillingApiException;
 import com.ning.billing.entitlement.api.user.EntitlementUserApi;
 import com.ning.billing.entitlement.api.user.Subscription;
 import com.ning.billing.entitlement.api.user.SubscriptionBundle;
@@ -108,27 +109,22 @@ public class DefaultBillingApi implements BillingApi {
         } catch (AccountApiException e) {
             log.warn("Failed while getting BillingEvent", e);
         }
-
         blockCalculator.insertBlockingEvents(result);
-
         return result;
     }
 
 
     @Override
-    public UUID getAccountIdFromSubscriptionId(UUID subscriptionId) {
-        return chargeThruApi.getAccountIdFromSubscriptionId(subscriptionId);
+    public UUID getAccountIdFromSubscriptionId(UUID subscriptionId) throws EntitlementBillingApiException {
+        UUID result = chargeThruApi.getAccountIdFromSubscriptionId(subscriptionId);
+        if (result == null) {
+            throw new EntitlementBillingApiException(ErrorCode.ENT_INVALID_SUBSCRIPTION_ID, subscriptionId.toString());
+        }
+        return result;
     }
 
     @Override
     public void setChargedThroughDate(UUID subscriptionId, DateTime ctd, CallContext context) {
         chargeThruApi.setChargedThroughDate(subscriptionId, ctd, context);
     }
-
-    @Override
-    public void setChargedThroughDateFromTransaction(Transmogrifier transactionalDao, UUID subscriptionId,
-            DateTime ctd, CallContext context) {
-        chargeThruApi.setChargedThroughDateFromTransaction(transactionalDao, subscriptionId, ctd, context);
-    }
-
 }
diff --git a/junction/src/test/java/com/ning/billing/junction/api/blocking/TestBlockingApi.java b/junction/src/test/java/com/ning/billing/junction/api/blocking/TestBlockingApi.java
index 1d814b8..db1d1e0 100644
--- a/junction/src/test/java/com/ning/billing/junction/api/blocking/TestBlockingApi.java
+++ b/junction/src/test/java/com/ning/billing/junction/api/blocking/TestBlockingApi.java
@@ -27,12 +27,10 @@ import org.testng.Assert;
 import org.testng.annotations.AfterClass;
 import org.testng.annotations.BeforeClass;
 import org.testng.annotations.BeforeMethod;
-import org.testng.annotations.BeforeTest;
 import org.testng.annotations.Guice;
 import org.testng.annotations.Test;
 
 import com.google.inject.Inject;
-import com.ning.billing.catalog.api.CatalogApiException;
 import com.ning.billing.dbi.MysqlTestingHelper;
 import com.ning.billing.entitlement.api.user.SubscriptionBundle;
 import com.ning.billing.junction.MockModule;
diff --git a/junction/src/test/java/com/ning/billing/junction/MockModule.java b/junction/src/test/java/com/ning/billing/junction/MockModule.java
index 5651c8b..23e18d7 100644
--- a/junction/src/test/java/com/ning/billing/junction/MockModule.java
+++ b/junction/src/test/java/com/ning/billing/junction/MockModule.java
@@ -16,24 +16,14 @@
 
 package com.ning.billing.junction;
 
-import org.skife.config.ConfigurationObjectFactory;
-import org.skife.jdbi.v2.IDBI;
-
 import com.ning.billing.catalog.glue.CatalogModule;
-import com.ning.billing.dbi.DBIProvider;
-import com.ning.billing.dbi.DbiConfig;
-import com.ning.billing.dbi.MysqlTestingHelper;
-import com.ning.billing.junction.glue.JunctionModule;
+import com.ning.billing.junction.glue.DefaultJunctionModule;
 import com.ning.billing.mock.glue.MockDbHelperModule;
-import com.ning.billing.util.callcontext.CallContextFactory;
-import com.ning.billing.util.callcontext.DefaultCallContextFactory;
-import com.ning.billing.util.clock.Clock;
-import com.ning.billing.util.clock.ClockMock;
 import com.ning.billing.util.clock.MockClockModule;
 import com.ning.billing.util.glue.CallContextModule;
 
 
-public class MockModule extends JunctionModule {
+public class MockModule extends DefaultJunctionModule {
     public static final String PLUGIN_NAME = "Booboo";
 
     @Override
@@ -47,12 +37,12 @@ public class MockModule extends JunctionModule {
     }
     
     @Override
-    protected void installBillingApi() {
+    public void installBillingApi() {
         // no billinggApi
     }
 
     @Override
-    protected void installAccountUserApi() {
+    public void installAccountUserApi() {
         
     }
 
diff --git a/junction/src/test/java/com/ning/billing/junction/plumbing/billing/MockSubscription.java b/junction/src/test/java/com/ning/billing/junction/plumbing/billing/MockSubscription.java
new file mode 100644
index 0000000..8c85596
--- /dev/null
+++ b/junction/src/test/java/com/ning/billing/junction/plumbing/billing/MockSubscription.java
@@ -0,0 +1,202 @@
+/*
+ * 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.junction.plumbing.billing;
+
+import java.util.List;
+import java.util.UUID;
+
+import com.ning.billing.util.dao.ObjectType;
+import org.joda.time.DateTime;
+
+import com.ning.billing.catalog.api.BillingPeriod;
+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.PriceList;
+import com.ning.billing.catalog.api.ProductCategory;
+import com.ning.billing.entitlement.api.user.EntitlementUserApiException;
+import com.ning.billing.entitlement.api.user.Subscription;
+import com.ning.billing.entitlement.api.user.SubscriptionEvent;
+import com.ning.billing.junction.api.BlockingState;
+import com.ning.billing.mock.BrainDeadProxyFactory;
+import com.ning.billing.util.callcontext.CallContext;
+import com.ning.billing.util.customfield.CustomField;
+import com.ning.billing.util.tag.ControlTagType;
+import com.ning.billing.util.tag.Tag;
+import com.ning.billing.util.tag.TagDefinition;
+
+
+public class MockSubscription implements Subscription {
+    Subscription sub = BrainDeadProxyFactory.createBrainDeadProxyFor(Subscription.class);
+
+    public List<Tag> getTagList() {
+        return sub.getTagList();
+    }
+
+    public UUID getId() {
+        return sub.getId();
+    }
+
+    public boolean hasTag(TagDefinition tagDefinition) {
+        return sub.hasTag(tagDefinition);
+    }
+
+    public String getFieldValue(String fieldName) {
+        return sub.getFieldValue(fieldName);
+    }
+
+    public boolean hasTag(ControlTagType controlTagType) {
+        return sub.hasTag(controlTagType);
+    }
+
+    public void setFieldValue(String fieldName, String fieldValue) {
+        sub.setFieldValue(fieldName, fieldValue);
+    }
+
+    public void addTag(TagDefinition definition) {
+        sub.addTag(definition);
+    }
+
+    public void addTags(List<Tag> tags) {
+        sub.addTags(tags);
+    }
+
+    public void saveFieldValue(String fieldName, String fieldValue, CallContext context) {
+        sub.saveFieldValue(fieldName, fieldValue, context);
+    }
+
+    public void addTagsFromDefinitions(List<TagDefinition> tagDefinitions) {
+        sub.addTagsFromDefinitions(tagDefinitions);
+    }
+
+    public List<CustomField> getFieldList() {
+        return sub.getFieldList();
+    }
+
+    public void clearTags() {
+        sub.clearTags();
+    }
+
+    public void setFields(List<CustomField> fields) {
+        sub.setFields(fields);
+    }
+
+    public void removeTag(TagDefinition definition) {
+        sub.removeTag(definition);
+    }
+
+    public void saveFields(List<CustomField> fields, CallContext context) {
+        sub.saveFields(fields, context);
+    }
+
+    public boolean generateInvoice() {
+        return sub.generateInvoice();
+    }
+
+    public boolean processPayment() {
+        return sub.processPayment();
+    }
+
+    public void clearFields() {
+        sub.clearFields();
+    }
+
+    public void clearPersistedFields(CallContext context) {
+        sub.clearPersistedFields(context);
+    }
+
+    @Override
+    public ObjectType getObjectType() {
+        return sub.getObjectType();
+    }
+
+    public boolean cancel(DateTime requestedDate, boolean eot, CallContext context) throws EntitlementUserApiException {
+        return sub.cancel(requestedDate, eot, context);
+    }
+
+    public boolean uncancel(CallContext context) throws EntitlementUserApiException {
+        return sub.uncancel(context);
+    }
+
+    public boolean changePlan(String productName, BillingPeriod term, String planSet, DateTime requestedDate,
+            CallContext context) throws EntitlementUserApiException {
+        return sub.changePlan(productName, term, planSet, requestedDate, context);
+    }
+
+    public boolean recreate(PlanPhaseSpecifier spec, DateTime requestedDate, CallContext context)
+            throws EntitlementUserApiException {
+        return sub.recreate(spec, requestedDate, context);
+    }
+
+    public UUID getBundleId() {
+        return sub.getBundleId();
+    }
+
+    public SubscriptionState getState() {
+        return sub.getState();
+    }
+
+    public DateTime getStartDate() {
+        return sub.getStartDate();
+    }
+
+    public DateTime getEndDate() {
+        return sub.getEndDate();
+    }
+
+    public Plan getCurrentPlan() {
+        return sub.getCurrentPlan();
+    }
+
+    public BlockingState getBlockingState() {
+        return sub.getBlockingState();
+    }
+
+    public PriceList getCurrentPriceList() {
+        return sub.getCurrentPriceList();
+    }
+
+    public PlanPhase getCurrentPhase() {
+        return sub.getCurrentPhase();
+    }
+
+    public DateTime getChargedThroughDate() {
+        return sub.getChargedThroughDate();
+    }
+
+    public DateTime getPaidThroughDate() {
+        return sub.getPaidThroughDate();
+    }
+
+    public ProductCategory getCategory() {
+        return sub.getCategory();
+    }
+
+    public SubscriptionEvent getPendingTransition() {
+        return sub.getPendingTransition();
+    }
+
+    public SubscriptionEvent getPreviousTransition() {
+        return sub.getPreviousTransition();
+    }
+
+    public List<SubscriptionEvent> getBillingTransitions() {
+        return sub.getBillingTransitions();
+    }
+    
+    
+}
diff --git a/junction/src/test/java/com/ning/billing/junction/plumbing/billing/MockSubscriptionEvent.java b/junction/src/test/java/com/ning/billing/junction/plumbing/billing/MockSubscriptionEvent.java
new file mode 100644
index 0000000..cbba255
--- /dev/null
+++ b/junction/src/test/java/com/ning/billing/junction/plumbing/billing/MockSubscriptionEvent.java
@@ -0,0 +1,333 @@
+/* 
+ * 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.junction.plumbing.billing;
+
+import java.util.UUID;
+
+import org.codehaus.jackson.annotate.JsonCreator;
+import org.codehaus.jackson.annotate.JsonIgnore;
+import org.codehaus.jackson.annotate.JsonProperty;
+import org.joda.time.DateTime;
+
+import com.ning.billing.entitlement.api.SubscriptionTransitionType;
+import com.ning.billing.entitlement.api.user.Subscription.SubscriptionState;
+import com.ning.billing.entitlement.api.user.SubscriptionEvent;
+import com.ning.billing.util.bus.BusEvent.BusEventType;
+
+public class MockSubscriptionEvent implements SubscriptionEvent {
+
+    private final Long totalOrdering;
+    private final UUID subscriptionId;
+    private final UUID bundleId;
+    private final UUID eventId;
+    private final DateTime requestedTransitionTime;
+    private final DateTime effectiveTransitionTime;
+    private final SubscriptionState previousState;
+    private final String previousPriceList;
+    private final String previousPlan;
+    private final String previousPhase;
+    private final SubscriptionState nextState;
+    private final String nextPriceList;
+    private final String nextPlan;
+    private final String nextPhase;
+    private final Integer remainingEventsForUserOperation;
+    private final UUID userToken;
+    private final SubscriptionTransitionType transitionType;
+
+    private final DateTime startDate;
+   
+    @JsonCreator
+    public MockSubscriptionEvent(@JsonProperty("eventId") UUID eventId,
+            @JsonProperty("subscriptionId") UUID subscriptionId,
+            @JsonProperty("bundleId") UUID bundleId,
+            @JsonProperty("requestedTransitionTime") DateTime requestedTransitionTime,
+            @JsonProperty("effectiveTransitionTime") DateTime effectiveTransitionTime,
+            @JsonProperty("previousState") SubscriptionState previousState,
+            @JsonProperty("previousPlan") String previousPlan,
+            @JsonProperty("previousPhase") String previousPhase,
+            @JsonProperty("previousPriceList") String previousPriceList,
+            @JsonProperty("nextState") SubscriptionState nextState,
+            @JsonProperty("nextPlan") String nextPlan,
+            @JsonProperty("nextPhase") String nextPhase,
+            @JsonProperty("nextPriceList") String nextPriceList,
+            @JsonProperty("totalOrdering") Long totalOrdering,
+            @JsonProperty("userToken") UUID userToken,
+            @JsonProperty("transitionType") SubscriptionTransitionType transitionType,
+            @JsonProperty("remainingEventsForUserOperation") Integer remainingEventsForUserOperation,
+            @JsonProperty("startDate") DateTime startDate) {
+        super();
+        this.eventId = eventId;
+        this.subscriptionId = subscriptionId;
+        this.bundleId = bundleId;
+        this.requestedTransitionTime = requestedTransitionTime;
+        this.effectiveTransitionTime = effectiveTransitionTime;
+        this.previousState = previousState;
+        this.previousPriceList = previousPriceList;
+        this.previousPlan = previousPlan;
+        this.previousPhase = previousPhase;
+        this.nextState = nextState;
+        this.nextPlan = nextPlan;
+        this.nextPriceList = nextPriceList;
+        this.nextPhase = nextPhase;
+        this.totalOrdering = totalOrdering;
+        this.userToken = userToken;
+        this.transitionType = transitionType;
+        this.remainingEventsForUserOperation = remainingEventsForUserOperation;
+        this.startDate = startDate;
+    }
+    
+    @JsonIgnore
+    @Override
+    public BusEventType getBusEventType() {
+        return BusEventType.SUBSCRIPTION_TRANSITION;
+    }
+
+    @JsonProperty("eventId")
+    @Override
+    public UUID getId() {
+        return eventId;
+    }
+
+    @Override
+    public UUID getSubscriptionId() {
+        return subscriptionId;
+    }
+
+    @Override
+    public UUID getBundleId() {
+        return bundleId;
+    }
+
+
+    @Override
+    public SubscriptionState getPreviousState() {
+        return previousState;
+    }
+
+    @Override
+    public String getPreviousPlan() {
+        return previousPlan;
+    }
+
+    @Override
+    public String getPreviousPhase() {
+        return previousPhase;
+    }
+
+    @Override
+    public String getNextPlan() {
+        return nextPlan;
+    }
+
+    @Override
+    public String getNextPhase() {
+        return nextPhase;
+    }
+
+    @Override
+    public SubscriptionState getNextState() {
+        return nextState;
+    }
+
+
+    @Override
+    public String getPreviousPriceList() {
+        return previousPriceList;
+    }
+
+    @Override
+    public String getNextPriceList() {
+        return nextPriceList;
+    }
+    
+    @Override
+    public UUID getUserToken() {
+        return userToken;
+    }
+    
+    @Override
+    public Integer getRemainingEventsForUserOperation() {
+        return remainingEventsForUserOperation;
+    }
+
+
+    @Override
+    public DateTime getRequestedTransitionTime() {
+        return requestedTransitionTime;
+    }
+
+    @Override
+    public DateTime getEffectiveTransitionTime() {
+        return effectiveTransitionTime;
+    }
+
+    @Override
+    public Long getTotalOrdering() {
+        return totalOrdering;
+    }
+
+    @Override
+    public SubscriptionTransitionType getTransitionType() {
+        return transitionType;
+    }
+    
+    @JsonProperty("startDate")
+    @Override
+    public DateTime getSubscriptionStartDate() {
+        return startDate;
+    }
+
+
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result
+                + ((bundleId == null) ? 0 : bundleId.hashCode());
+        result = prime
+                * result
+                + ((effectiveTransitionTime == null) ? 0
+                        : effectiveTransitionTime.hashCode());
+        result = prime * result + ((eventId == null) ? 0 : eventId.hashCode());
+        result = prime * result
+                + ((nextPhase == null) ? 0 : nextPhase.hashCode());
+        result = prime * result
+                + ((nextPlan == null) ? 0 : nextPlan.hashCode());
+        result = prime * result
+                + ((nextPriceList == null) ? 0 : nextPriceList.hashCode());
+        result = prime * result
+                + ((nextState == null) ? 0 : nextState.hashCode());
+        result = prime * result
+                + ((previousPhase == null) ? 0 : previousPhase.hashCode());
+        result = prime * result
+                + ((previousPlan == null) ? 0 : previousPlan.hashCode());
+        result = prime
+                * result
+                + ((previousPriceList == null) ? 0 : previousPriceList
+                        .hashCode());
+        result = prime * result
+                + ((previousState == null) ? 0 : previousState.hashCode());
+        result = prime
+                * result
+                + ((remainingEventsForUserOperation == null) ? 0
+                        : remainingEventsForUserOperation.hashCode());
+        result = prime
+                * result
+                + ((requestedTransitionTime == null) ? 0
+                        : requestedTransitionTime.hashCode());
+        result = prime * result
+                + ((subscriptionId == null) ? 0 : subscriptionId.hashCode());
+        result = prime * result
+                + ((totalOrdering == null) ? 0 : totalOrdering.hashCode());
+        result = prime * result
+                + ((transitionType == null) ? 0 : transitionType.hashCode());
+        result = prime * result
+                + ((userToken == null) ? 0 : userToken.hashCode());
+        return result;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj)
+            return true;
+        if (obj == null)
+            return false;
+        if (getClass() != obj.getClass())
+            return false;
+        MockSubscriptionEvent other = (MockSubscriptionEvent) obj;
+        if (bundleId == null) {
+            if (other.bundleId != null)
+                return false;
+        } else if (!bundleId.equals(other.bundleId))
+            return false;
+        if (effectiveTransitionTime == null) {
+            if (other.effectiveTransitionTime != null)
+                return false;
+        } else if (effectiveTransitionTime
+                .compareTo(other.effectiveTransitionTime) != 0)
+            return false;
+        if (eventId == null) {
+            if (other.eventId != null)
+                return false;
+        } else if (!eventId.equals(other.eventId))
+            return false;
+        if (nextPhase == null) {
+            if (other.nextPhase != null)
+                return false;
+        } else if (!nextPhase.equals(other.nextPhase))
+            return false;
+        if (nextPlan == null) {
+            if (other.nextPlan != null)
+                return false;
+        } else if (!nextPlan.equals(other.nextPlan))
+            return false;
+        if (nextPriceList == null) {
+            if (other.nextPriceList != null)
+                return false;
+        } else if (!nextPriceList.equals(other.nextPriceList))
+            return false;
+        if (nextState != other.nextState)
+            return false;
+        if (previousPhase == null) {
+            if (other.previousPhase != null)
+                return false;
+        } else if (!previousPhase.equals(other.previousPhase))
+            return false;
+        if (previousPlan == null) {
+            if (other.previousPlan != null)
+                return false;
+        } else if (!previousPlan.equals(other.previousPlan))
+            return false;
+        if (previousPriceList == null) {
+            if (other.previousPriceList != null)
+                return false;
+        } else if (!previousPriceList.equals(other.previousPriceList))
+            return false;
+        if (previousState != other.previousState)
+            return false;
+        if (remainingEventsForUserOperation == null) {
+            if (other.remainingEventsForUserOperation != null)
+                return false;
+        } else if (!remainingEventsForUserOperation
+                .equals(other.remainingEventsForUserOperation))
+            return false;
+        if (requestedTransitionTime == null) {
+            if (other.requestedTransitionTime != null)
+                return false;
+        } else if (requestedTransitionTime
+                .compareTo(other.requestedTransitionTime) != 0)
+            return false;
+        if (subscriptionId == null) {
+            if (other.subscriptionId != null)
+                return false;
+        } else if (!subscriptionId.equals(other.subscriptionId))
+            return false;
+        if (totalOrdering == null) {
+            if (other.totalOrdering != null)
+                return false;
+        } else if (!totalOrdering.equals(other.totalOrdering))
+            return false;
+        if (transitionType != other.transitionType)
+            return false;
+        if (userToken == null) {
+            if (other.userToken != null)
+                return false;
+        } else if (!userToken.equals(other.userToken))
+            return false;
+        return true;
+    }
+    
+}
diff --git a/junction/src/test/java/com/ning/billing/junction/plumbing/billing/TestDefaultEntitlementBillingApi.java b/junction/src/test/java/com/ning/billing/junction/plumbing/billing/TestDefaultEntitlementBillingApi.java
index c30e906..0d0d14b 100644
--- a/junction/src/test/java/com/ning/billing/junction/plumbing/billing/TestDefaultEntitlementBillingApi.java
+++ b/junction/src/test/java/com/ning/billing/junction/plumbing/billing/TestDefaultEntitlementBillingApi.java
@@ -51,22 +51,14 @@ import com.ning.billing.catalog.api.PlanPhase;
 import com.ning.billing.catalog.api.Price;
 import com.ning.billing.catalog.api.PriceList;
 import com.ning.billing.catalog.api.PriceListSet;
+import com.ning.billing.entitlement.api.SubscriptionTransitionType;
 import com.ning.billing.entitlement.api.billing.BillingEvent;
 import com.ning.billing.entitlement.api.billing.BillingModeType;
-import com.ning.billing.entitlement.api.user.DefaultSubscriptionEvent;
-
-import com.ning.billing.entitlement.api.user.DefaultSubscriptionFactory.SubscriptionBuilder;
 import com.ning.billing.entitlement.api.user.EntitlementUserApi;
 import com.ning.billing.entitlement.api.user.Subscription;
 import com.ning.billing.entitlement.api.user.Subscription.SubscriptionState;
 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.SubscriptionEvent;
-
-import com.ning.billing.entitlement.api.user.SubscriptionTransitionData;
-import com.ning.billing.entitlement.events.EntitlementEvent.EventType;
-import com.ning.billing.entitlement.events.user.ApiEventType;
 import com.ning.billing.junction.api.BillingApi;
 import com.ning.billing.junction.api.Blockable;
 import com.ning.billing.junction.api.Blockable.Type;
@@ -147,6 +139,7 @@ public class TestDefaultEntitlementBillingApi {
 	private Clock clock;
 	private Subscription subscription;
 	private DateTime subscriptionStartDate;
+	private Plan subscriptionPlan;
 
 	@BeforeSuite(groups={"fast", "slow"})
 	public void setup() throws ServiceException {
@@ -157,21 +150,44 @@ public class TestDefaultEntitlementBillingApi {
 	@BeforeMethod(groups={"fast", "slow"})
 	public void setupEveryTime() {
 		bundles = new ArrayList<SubscriptionBundle>();
-		final SubscriptionBundle bundle = new SubscriptionBundleData( eventId,"TestKey", subId,  clock.getUTCNow().minusDays(4), null);
+		final SubscriptionBundle bundle = BrainDeadProxyFactory.createBrainDeadProxyFor(SubscriptionBundle.class);
+		((ZombieControl)bundle).addResult("getId", eventId);
+		        
+		        //new SubscriptionBundleData( eventId,"TestKey", subId,  clock.getUTCNow().minusDays(4), null);
 		bundles.add(bundle);
 
 
 		subscriptionTransitions = new LinkedList<SubscriptionEvent>();
 		subscriptions = new LinkedList<Subscription>();
 
-		SubscriptionBuilder builder = new SubscriptionBuilder();
 		subscriptionStartDate = clock.getUTCNow().minusDays(3);
-		builder.setStartDate(subscriptionStartDate).setId(subId).setBundleId(bunId);
-		subscription = new SubscriptionData(builder) {
+		subscription = new MockSubscription() {
 		    @Override
             public List<SubscriptionEvent> getBillingTransitions() {
 		    	return subscriptionTransitions;
 		    }
+
+            @Override
+            public Plan getCurrentPlan() {
+                return subscriptionPlan;
+            }
+
+            @Override
+            public UUID getId() {
+                return subId;
+            }
+
+            @Override
+            public UUID getBundleId() {
+                return bunId;
+            }
+
+            @Override
+            public DateTime getStartDate() {
+                return subscriptionStartDate;
+            }
+            
+		    
 		};
 
 		subscriptions.add(subscription);
@@ -212,9 +228,12 @@ public class TestDefaultEntitlementBillingApi {
 		PlanPhase nextPhase = nextPlan.getAllPhases()[0]; // The trial has no billing period
         PriceList nextPriceList = catalogService.getFullCatalog().findPriceList(PriceListSet.DEFAULT_PRICELIST_NAME, now);
 
-		SubscriptionEvent t = new DefaultSubscriptionEvent(new SubscriptionTransitionData(
-				eventId, subId, bunId, EventType.API_USER, ApiEventType.CREATE, then, now, null, null, null, null,
-				SubscriptionState.ACTIVE, nextPlan, nextPhase, nextPriceList, 1L, null, true), then);
+        SubscriptionEvent t = new MockSubscriptionEvent(
+                eventId, subId, bunId, then, now, null, null, null, null, SubscriptionState.ACTIVE, 
+                nextPlan.getName(), nextPhase.getName(), 
+                nextPriceList.getName(), 1L,null, 
+                SubscriptionTransitionType.CREATE, 0, null); 
+
 		subscriptionTransitions.add(t);
 
         AccountUserApi accountApi = BrainDeadProxyFactory.createBrainDeadProxyFor(AccountUserApi.class);
@@ -228,7 +247,7 @@ public class TestDefaultEntitlementBillingApi {
         BillingApi api = new DefaultBillingApi(null, factory, accountApi, bcdCalculator, entitlementApi, blockCalculator, catalogService);
         SortedSet<BillingEvent> events = api.getBillingEventsForAccountAndUpdateAccountBCD(new UUID(0L,0L));
 
-		checkFirstEvent(events, nextPlan, 32, subId, now, nextPhase, ApiEventType.CREATE.toString());
+		checkFirstEvent(events, nextPlan, 32, subId, now, nextPhase, SubscriptionTransitionType.CREATE.toString());
 	}
 
     @Test(enabled=false, groups="fast")
@@ -238,9 +257,12 @@ public class TestDefaultEntitlementBillingApi {
 		Plan nextPlan = catalogService.getFullCatalog().findPlan("PickupTrialEvergreen10USD", now);
 		PlanPhase nextPhase = nextPlan.getAllPhases()[1];
 		PriceList nextPriceList = catalogService.getFullCatalog().findPriceList(PriceListSet.DEFAULT_PRICELIST_NAME, now);
-		SubscriptionEvent t = new DefaultSubscriptionEvent(new SubscriptionTransitionData(
-		        eventId, subId, bunId, EventType.API_USER, ApiEventType.CREATE, then, now, null, null, null, null, SubscriptionState.ACTIVE, 
-				nextPlan, nextPhase, nextPriceList, 1L, null, true), then);
+        SubscriptionEvent t = new MockSubscriptionEvent(
+                eventId, subId, bunId, then, now, null, null, null, null, SubscriptionState.ACTIVE, 
+                nextPlan.getName(), nextPhase.getName(), 
+                nextPriceList.getName(), 1L,null, 
+                SubscriptionTransitionType.CREATE, 0, null); 
+
 		subscriptionTransitions.add(t);
 
 		Account account = BrainDeadProxyFactory.createBrainDeadProxyFor(Account.class);
@@ -258,7 +280,7 @@ public class TestDefaultEntitlementBillingApi {
         BillingApi api = new DefaultBillingApi(null, factory, accountApi, bcdCalculator, entitlementApi, blockCalculator, catalogService);
         SortedSet<BillingEvent> events = api.getBillingEventsForAccountAndUpdateAccountBCD(new UUID(0L,0L));
 
-		checkFirstEvent(events, nextPlan, subscription.getStartDate().getDayOfMonth(), subId, now, nextPhase, ApiEventType.CREATE.toString());
+		checkFirstEvent(events, nextPlan, subscription.getStartDate().plusDays(30).getDayOfMonth(), subId, now, nextPhase, SubscriptionTransitionType.CREATE.toString());
 	}
 
     @Test(enabled=true, groups="fast")
@@ -269,9 +291,13 @@ public class TestDefaultEntitlementBillingApi {
 		PlanPhase nextPhase = nextPlan.getAllPhases()[1];
         PriceList nextPriceList = catalogService.getFullCatalog().findPriceList(PriceListSet.DEFAULT_PRICELIST_NAME, now);
 
-		SubscriptionEvent t = new DefaultSubscriptionEvent(new SubscriptionTransitionData(
-		        eventId, subId, bunId, EventType.API_USER, ApiEventType.CREATE, then, now, null, null, null, null, SubscriptionState.ACTIVE, nextPlan, nextPhase, nextPriceList,
-				1L, null, true), then);
+        SubscriptionEvent t = new MockSubscriptionEvent(
+                eventId, subId, bunId, then, now, null, null, null, null, SubscriptionState.ACTIVE, 
+                nextPlan.getName(), nextPhase.getName(), 
+                nextPriceList.getName(), 1L,null, 
+                SubscriptionTransitionType.CREATE, 0, null); 
+
+
 		subscriptionTransitions.add(t);
 
         AccountUserApi accountApi = BrainDeadProxyFactory.createBrainDeadProxyFor(AccountUserApi.class);
@@ -288,7 +314,7 @@ public class TestDefaultEntitlementBillingApi {
 
         SortedSet<BillingEvent> events = api.getBillingEventsForAccountAndUpdateAccountBCD(new UUID(0L,0L));
 
-		checkFirstEvent(events, nextPlan, 32, subId, now, nextPhase, ApiEventType.CREATE.toString());
+		checkFirstEvent(events, nextPlan, 32, subId, now, nextPhase, SubscriptionTransitionType.CREATE.toString());
 	}
 
     @Test(enabled=false, groups="fast")
@@ -299,9 +325,12 @@ public class TestDefaultEntitlementBillingApi {
 		PlanPhase nextPhase = nextPlan.getAllPhases()[0];
         PriceList nextPriceList = catalogService.getFullCatalog().findPriceList(PriceListSet.DEFAULT_PRICELIST_NAME, now);
 
-		SubscriptionEvent t = new DefaultSubscriptionEvent(new SubscriptionTransitionData(
-		        eventId, subId, bunId, EventType.API_USER, ApiEventType.CREATE, then, now, null, null, null, null, SubscriptionState.ACTIVE, nextPlan, nextPhase, nextPriceList, 1L,
-				null, true), then);
+        SubscriptionEvent t = new MockSubscriptionEvent(
+                eventId, subId, bunId, then, now, null, null, null, null, SubscriptionState.ACTIVE, 
+                nextPlan.getName(), nextPhase.getName(), 
+                nextPriceList.getName(), 1L,null, 
+                SubscriptionTransitionType.CREATE, 0, null); 
+
 		subscriptionTransitions.add(t);
 
 		Account account = BrainDeadProxyFactory.createBrainDeadProxyFor(Account.class);
@@ -315,11 +344,13 @@ public class TestDefaultEntitlementBillingApi {
         
         BillCycleDayCalculator bcdCalculator = new BillCycleDayCalculator(catalogService, entitlementApi);
         CallContextFactory factory = new DefaultCallContextFactory(clock);
-
+        
         BillingApi api = new DefaultBillingApi(null, factory, accountApi, bcdCalculator, entitlementApi, blockCalculator, catalogService);
+        subscriptionPlan = catalogService.getFullCatalog().findPlan("PickupTrialEvergreen10USD", now);
+
         SortedSet<BillingEvent> events = api.getBillingEventsForAccountAndUpdateAccountBCD(new UUID(0L,0L));
 
-		checkFirstEvent(events, nextPlan, subscription.getStartDate().plusDays(30).getDayOfMonth(), subId, now, nextPhase, ApiEventType.CREATE.toString());
+		checkFirstEvent(events, nextPlan, subscription.getStartDate().plusDays(30).getDayOfMonth(), subId, now, nextPhase, SubscriptionTransitionType.CREATE.toString());
 	}
 
     @Test(enabled=true, groups="fast")
@@ -329,8 +360,14 @@ public class TestDefaultEntitlementBillingApi {
         Plan nextPlan = catalogService.getFullCatalog().findPlan("PickupTrialEvergreen10USD", now);
         PlanPhase nextPhase = nextPlan.getAllPhases()[1];
         PriceList nextPriceList = catalogService.getFullCatalog().findPriceList(PriceListSet.DEFAULT_PRICELIST_NAME, now);
-        SubscriptionEvent t =  new DefaultSubscriptionEvent(new SubscriptionTransitionData(
-                eventId, subId, bunId, EventType.API_USER, ApiEventType.CREATE, then, now, null, null, null, null, SubscriptionState.ACTIVE, nextPlan, nextPhase, nextPriceList, 1L, null, true), then);
+
+        
+        SubscriptionEvent t = new MockSubscriptionEvent(
+                eventId, subId, bunId, then, now, null, null, null, null, SubscriptionState.ACTIVE, 
+                nextPlan.getName(), nextPhase.getName(), 
+                nextPriceList.getName(), 1L,null, 
+                SubscriptionTransitionType.CREATE, 0, null); 
+
         subscriptionTransitions.add(t);
 
         AccountUserApi accountApi = BrainDeadProxyFactory.createBrainDeadProxyFor(AccountUserApi.class);
@@ -383,9 +420,9 @@ public class TestDefaultEntitlementBillingApi {
         Assert.assertEquals(events.size(), 3);
         Iterator<BillingEvent> it = events.iterator();
        
-        checkEvent(it.next(), nextPlan, 32, subId, now, nextPhase, ApiEventType.CREATE.toString(), nextPhase.getFixedPrice(), nextPhase.getRecurringPrice());
-        checkEvent(it.next(), nextPlan, 32, subId, now.plusDays(1), nextPhase, ApiEventType.CANCEL.toString(), new MockPrice("0"), new MockPrice("0"));
-        checkEvent(it.next(), nextPlan, 32, subId, now.plusDays(2), nextPhase, ApiEventType.RE_CREATE.toString(), nextPhase.getFixedPrice(), nextPhase.getRecurringPrice());
+        checkEvent(it.next(), nextPlan, 32, subId, now, nextPhase, SubscriptionTransitionType.CREATE.toString(), nextPhase.getFixedPrice(), nextPhase.getRecurringPrice());
+        checkEvent(it.next(), nextPlan, 32, subId, now.plusDays(1), nextPhase, SubscriptionTransitionType.CANCEL.toString(), new MockPrice("0"), new MockPrice("0"));
+        checkEvent(it.next(), nextPlan, 32, subId, now.plusDays(2), nextPhase, SubscriptionTransitionType.RE_CREATE.toString(), nextPhase.getFixedPrice(), nextPhase.getRecurringPrice());
         
     }
 

overdue/pom.xml 21(+1 -20)

diff --git a/overdue/pom.xml b/overdue/pom.xml
index b6f64a0..2182ffc 100644
--- a/overdue/pom.xml
+++ b/overdue/pom.xml
@@ -55,19 +55,7 @@
             <artifactId>commons-io</artifactId>
             <scope>test</scope>
         </dependency>
-        <!-- Check if we need this one -->
-        <dependency>
-            <groupId>com.ning.billing</groupId>
-            <artifactId>killbill-junction</artifactId>
-            <scope>test</scope>
-         </dependency>
-         <dependency>
-            <groupId>com.ning.billing</groupId>
-            <artifactId>killbill-junction</artifactId>
-            <type>test-jar</type>
-            <scope>test</scope>
-        </dependency>
-        
+                
         <dependency>
             <groupId>com.ning.billing</groupId>
             <artifactId>killbill-util</artifactId>
@@ -117,13 +105,6 @@
         <plugins>
             <plugin>
                 <groupId>org.apache.maven.plugins</groupId>
-                <artifactId>maven-surefire-plugin</artifactId>
-                <configuration>
-                    <groups>fast,slow, stress</groups>
-                </configuration>
-            </plugin>
-            <plugin>
-                <groupId>org.apache.maven.plugins</groupId>
                 <artifactId>maven-jar-plugin</artifactId>
                 <executions>
                     <execution>
diff --git a/overdue/src/main/java/com/ning/billing/overdue/api/DefaultOverdueUserApi.java b/overdue/src/main/java/com/ning/billing/overdue/api/DefaultOverdueUserApi.java
index 092911d..8a9ea68 100644
--- a/overdue/src/main/java/com/ning/billing/overdue/api/DefaultOverdueUserApi.java
+++ b/overdue/src/main/java/com/ning/billing/overdue/api/DefaultOverdueUserApi.java
@@ -22,6 +22,7 @@ import com.google.inject.Inject;
 import com.ning.billing.ErrorCode;
 import com.ning.billing.catalog.api.CatalogApiException;
 import com.ning.billing.catalog.api.CatalogService;
+import com.ning.billing.entitlement.api.user.EntitlementUserApiException;
 import com.ning.billing.entitlement.api.user.SubscriptionBundle;
 import com.ning.billing.junction.api.Blockable;
 import com.ning.billing.junction.api.BlockingApi;
@@ -35,7 +36,7 @@ import com.ning.billing.overdue.service.ExtendedOverdueService;
 import com.ning.billing.overdue.wrapper.OverdueWrapper;
 import com.ning.billing.overdue.wrapper.OverdueWrapperFactory;
 
-public class DefaultOverdueUserApi implements OverdueUserApi{
+public class DefaultOverdueUserApi implements OverdueUserApi { 
 
     
     private final OverdueWrapperFactory factory;
@@ -62,7 +63,7 @@ public class DefaultOverdueUserApi implements OverdueUserApi{
     }
     
     @Override
-    public <T extends Blockable> OverdueState<T> refreshOverdueStateFor(T overdueable) throws OverdueError, CatalogApiException {
+    public <T extends Blockable> OverdueState<T> refreshOverdueStateFor(T overdueable) throws OverdueError, CatalogApiException, EntitlementUserApiException {
         OverdueWrapper<T> wrapper = factory.createOverdueWrapperFor(overdueable);
         return wrapper.refresh();
     } 
diff --git a/overdue/src/main/java/com/ning/billing/overdue/calculator/BillingStateCalculator.java b/overdue/src/main/java/com/ning/billing/overdue/calculator/BillingStateCalculator.java
index 640ca9a..d47f3f3 100644
--- a/overdue/src/main/java/com/ning/billing/overdue/calculator/BillingStateCalculator.java
+++ b/overdue/src/main/java/com/ning/billing/overdue/calculator/BillingStateCalculator.java
@@ -27,6 +27,7 @@ import java.util.UUID;
 import org.joda.time.DateTime;
 
 import com.google.inject.Inject;
+import com.ning.billing.entitlement.api.user.EntitlementUserApiException;
 import com.ning.billing.invoice.api.Invoice;
 import com.ning.billing.invoice.api.InvoiceUserApi;
 import com.ning.billing.junction.api.Blockable;
@@ -56,7 +57,7 @@ public abstract class BillingStateCalculator<T extends Blockable> {
         this.clock = clock;
     }
     
-    public abstract BillingState<T> calculateBillingState(T overdueable);
+    public abstract BillingState<T> calculateBillingState(T overdueable) throws EntitlementUserApiException;
     
     protected DateTime earliest(SortedSet<Invoice> unpaidInvoices) {
         return unpaidInvoices.first().getInvoiceDate();
diff --git a/overdue/src/main/java/com/ning/billing/overdue/calculator/BillingStateCalculatorBundle.java b/overdue/src/main/java/com/ning/billing/overdue/calculator/BillingStateCalculatorBundle.java
index 544420f..89a7cc6 100644
--- a/overdue/src/main/java/com/ning/billing/overdue/calculator/BillingStateCalculatorBundle.java
+++ b/overdue/src/main/java/com/ning/billing/overdue/calculator/BillingStateCalculatorBundle.java
@@ -29,6 +29,7 @@ import com.ning.billing.catalog.api.PhaseType;
 import com.ning.billing.catalog.api.PriceList;
 import com.ning.billing.catalog.api.Product;
 import com.ning.billing.entitlement.api.user.EntitlementUserApi;
+import com.ning.billing.entitlement.api.user.EntitlementUserApiException;
 import com.ning.billing.entitlement.api.user.Subscription;
 import com.ning.billing.entitlement.api.user.SubscriptionBundle;
 import com.ning.billing.invoice.api.Invoice;
@@ -50,7 +51,7 @@ public class BillingStateCalculatorBundle  extends BillingStateCalculator<Subscr
     }
     
     @Override
-    public BillingStateBundle calculateBillingState(SubscriptionBundle bundle) {
+    public BillingStateBundle calculateBillingState(SubscriptionBundle bundle) throws EntitlementUserApiException {
         
         SortedSet<Invoice> unpaidInvoices = unpaidInvoicesForBundle(bundle.getId(), bundle.getAccountId());
  
diff --git a/overdue/src/main/java/com/ning/billing/overdue/config/DefaultOverdueState.java b/overdue/src/main/java/com/ning/billing/overdue/config/DefaultOverdueState.java
index 7d84e7c..9a448c2 100644
--- a/overdue/src/main/java/com/ning/billing/overdue/config/DefaultOverdueState.java
+++ b/overdue/src/main/java/com/ning/billing/overdue/config/DefaultOverdueState.java
@@ -42,12 +42,12 @@ public class DefaultOverdueState<T extends Blockable> extends ValidatingConfig<O
 
 	@XmlElement(required=false, name="externalMessage")
 	private String externalMessage = "";
-
-    @XmlElement(required=false, name="disableEntitlementAndChangesBlocked")
-    private Boolean disableEntitlement = false;
     
     @XmlElement(required=false, name="blockChanges")
     private Boolean blockChanges = false;
+
+    @XmlElement(required=false, name="disableEntitlementAndChangesBlocked")
+    private Boolean disableEntitlement = false;
     
     @XmlElement(required=false, name="daysBetweenPaymentRetries")
     private Integer daysBetweenPaymentRetries = 8;
diff --git a/overdue/src/main/java/com/ning/billing/overdue/wrapper/OverdueWrapper.java b/overdue/src/main/java/com/ning/billing/overdue/wrapper/OverdueWrapper.java
index 892ad05..0223803 100644
--- a/overdue/src/main/java/com/ning/billing/overdue/wrapper/OverdueWrapper.java
+++ b/overdue/src/main/java/com/ning/billing/overdue/wrapper/OverdueWrapper.java
@@ -17,6 +17,7 @@
 package com.ning.billing.overdue.wrapper;
 
 import com.ning.billing.catalog.api.CatalogApiException;
+import com.ning.billing.entitlement.api.user.EntitlementUserApiException;
 import com.ning.billing.junction.api.Blockable;
 import com.ning.billing.junction.api.BlockingApi;
 import com.ning.billing.overdue.OverdueState;
@@ -48,12 +49,12 @@ public class OverdueWrapper<T extends Blockable> {
         this.overdueStateApplicator = overdueStateApplicator;
     }
 
-    public OverdueState<T> refresh() throws OverdueError, CatalogApiException {
+    public OverdueState<T> refresh() throws OverdueError, CatalogApiException, EntitlementUserApiException {
         OverdueState<T> nextOverdueState;
         BillingState<T> billingState = billingStateCalcuator.calculateBillingState(overdueable);
         String previousOverdueStateName = api.getBlockingStateFor(overdueable).getStateName();
         nextOverdueState = overdueStateSet.calculateOverdueState(billingState, clock.getUTCNow());
-        if(!previousOverdueStateName.equals(nextOverdueState.getName())) {
+        if (!previousOverdueStateName.equals(nextOverdueState.getName())) {
             overdueStateApplicator.apply(overdueable, nextOverdueState, nextOverdueState, overdueStateSet.dateOfNextCheck(billingState, clock.getUTCNow())); 
         }
 
diff --git a/overdue/src/test/java/com/ning/billing/overdue/calculator/TestBillingStateCalculatorBundle.java b/overdue/src/test/java/com/ning/billing/overdue/calculator/TestBillingStateCalculatorBundle.java
index 3d5b855..bb7ece3 100644
--- a/overdue/src/test/java/com/ning/billing/overdue/calculator/TestBillingStateCalculatorBundle.java
+++ b/overdue/src/test/java/com/ning/billing/overdue/calculator/TestBillingStateCalculatorBundle.java
@@ -86,7 +86,8 @@ public class TestBillingStateCalculatorBundle extends TestBillingStateCalculator
     }
     
     @Test(groups = {"fast"}, enabled=true)
-    public void testcalculateBillingStateForBundle() {
+    public void testcalculateBillingStateForBundle() throws Exception {
+        
        UUID thisBundleId = new UUID(0L,0L);
        UUID thatBundleId = new UUID(0L,1L);
        
diff --git a/overdue/src/test/java/com/ning/billing/overdue/config/MockOverdueRules.java b/overdue/src/test/java/com/ning/billing/overdue/config/MockOverdueRules.java
index 3095296..514c85f 100644
--- a/overdue/src/test/java/com/ning/billing/overdue/config/MockOverdueRules.java
+++ b/overdue/src/test/java/com/ning/billing/overdue/config/MockOverdueRules.java
@@ -18,15 +18,15 @@ package com.ning.billing.overdue.config;
 
 
 import com.ning.billing.entitlement.api.user.SubscriptionBundle;
-import com.ning.billing.junction.MockBlockingModule;
 
 
 public class MockOverdueRules extends OverdueConfig {
+    public static final String CLEAR_STATE="Clear";
 
     @SuppressWarnings("unchecked")
     public MockOverdueRules() {
         OverdueStatesBundle bundleODS =  new OverdueStatesBundle();
-        bundleODS.setBundleOverdueStates(new DefaultOverdueState[] { new DefaultOverdueState<SubscriptionBundle>().setName(MockBlockingModule.CLEAR_STATE) });
+        bundleODS.setBundleOverdueStates(new DefaultOverdueState[] { new DefaultOverdueState<SubscriptionBundle>().setName(CLEAR_STATE) });
         setOverdueStatesBundle(bundleODS);
 
     }
diff --git a/overdue/src/test/java/com/ning/billing/overdue/config/MockOverdueState.java b/overdue/src/test/java/com/ning/billing/overdue/config/MockOverdueState.java
index 606556f..d1b9ab1 100644
--- a/overdue/src/test/java/com/ning/billing/overdue/config/MockOverdueState.java
+++ b/overdue/src/test/java/com/ning/billing/overdue/config/MockOverdueState.java
@@ -16,13 +16,12 @@
 
 package com.ning.billing.overdue.config;
 
-import com.ning.billing.junction.MockBlockingModule;
 import com.ning.billing.junction.api.Blockable;
 
 public class MockOverdueState<T extends Blockable> extends DefaultOverdueState<T> {
     
     public MockOverdueState() {
-        setName(MockBlockingModule.CLEAR_STATE);
+        setName(MockOverdueRules.CLEAR_STATE);
     }
 
     public MockOverdueState(String name, boolean blockChanges, boolean disableEntitlementAndBlockChanges) {
diff --git a/overdue/src/test/java/com/ning/billing/overdue/config/TestOverdueConfig.java b/overdue/src/test/java/com/ning/billing/overdue/config/TestOverdueConfig.java
new file mode 100644
index 0000000..df4f4c3
--- /dev/null
+++ b/overdue/src/test/java/com/ning/billing/overdue/config/TestOverdueConfig.java
@@ -0,0 +1,60 @@
+/*
+ * 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.overdue.config;
+
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+
+import org.testng.annotations.Test;
+
+import com.ning.billing.util.config.XMLLoader;
+
+public class TestOverdueConfig {
+    private String xml = 
+            "<overdueConfig>" +
+                    "   <bundleOverdueStates>" +
+                    "       <state name=\"OD1\">" +
+                    "           <condition>" +
+                    "               <timeSinceEarliestUnpaidInvoiceEqualsOrExceeds>" +
+                    "                   <unit>MONTHS</unit><number>1</number>" +
+                    "               </timeSinceEarliestUnpaidInvoiceEqualsOrExceeds>" +
+                    "           </condition>" +
+                    "           <externalMessage>Reached OD1</externalMessage>" +
+                    "           <blockChanges>true</blockChanges>" +
+                    "           <disableEntitlementAndChangesBlocked>false</disableEntitlementAndChangesBlocked>" +
+                    "       </state>" +
+                    "       <state name=\"OD2\">" +
+                    "           <condition>" +
+                    "               <timeSinceEarliestUnpaidInvoiceEqualsOrExceeds>" +
+                    "                   <unit>MONTHS</unit><number>2</number>" +
+                    "               </timeSinceEarliestUnpaidInvoiceEqualsOrExceeds>" +
+                    "           </condition>" +
+                    "           <externalMessage>Reached OD1</externalMessage>" +
+                    "           <blockChanges>true</blockChanges>" +
+                    "           <disableEntitlementAndChangesBlocked>true</disableEntitlementAndChangesBlocked>" +
+                    "       </state>" +
+                    "   </bundleOverdueStates>" +
+                    "</overdueConfig>";
+
+    @Test
+    public void testParseConfig() throws Exception {
+        InputStream is = new ByteArrayInputStream(xml.getBytes());
+        OverdueConfig c = XMLLoader.getObjectFromStreamNoValidation(is,  OverdueConfig.class);
+
+    }
+
+}
diff --git a/overdue/src/test/java/com/ning/billing/overdue/notification/TestOverdueCheckNotifier.java b/overdue/src/test/java/com/ning/billing/overdue/notification/TestOverdueCheckNotifier.java
index 3086274..dca8cb7 100644
--- a/overdue/src/test/java/com/ning/billing/overdue/notification/TestOverdueCheckNotifier.java
+++ b/overdue/src/test/java/com/ning/billing/overdue/notification/TestOverdueCheckNotifier.java
@@ -38,7 +38,6 @@ import org.testng.annotations.Test;
 import com.google.inject.Guice;
 import com.google.inject.Injector;
 import com.google.inject.Stage;
-import com.ning.billing.account.api.AccountUserApi;
 import com.ning.billing.catalog.DefaultCatalogService;
 import com.ning.billing.catalog.api.CatalogService;
 import com.ning.billing.config.CatalogConfig;
@@ -47,9 +46,7 @@ import com.ning.billing.dbi.MysqlTestingHelper;
 import com.ning.billing.entitlement.api.billing.ChargeThruApi;
 import com.ning.billing.entitlement.api.user.EntitlementUserApi;
 import com.ning.billing.entitlement.api.user.Subscription;
-import com.ning.billing.junction.api.BillingApi;
 import com.ning.billing.junction.api.Blockable;
-import com.ning.billing.junction.plumbing.billing.DefaultBillingApi;
 import com.ning.billing.lifecycle.KillbillService.ServiceException;
 import com.ning.billing.mock.BrainDeadProxyFactory;
 import com.ning.billing.mock.BrainDeadProxyFactory.ZombieControl;
@@ -141,7 +138,7 @@ public class TestOverdueCheckNotifier {
 
         clock = g.getInstance(Clock.class);
         IDBI dbi = g.getInstance(IDBI.class);
-        dao = dbi.onDemand(DummySqlTest.class);
+
         eventBus = g.getInstance(Bus.class);
         helper = g.getInstance(MysqlTestingHelper.class);
         notificationQueueService = g.getInstance(NotificationQueueService.class);
@@ -161,7 +158,7 @@ public class TestOverdueCheckNotifier {
         eventBus.start();
         notifier.initialize();
         notifier.start();
-
+        dao = dbi.onDemand(DummySqlTest.class);
 	}
 
 	private void startMysql() throws IOException, ClassNotFoundException, SQLException {

payment/pom.xml 28(+11 -17)

diff --git a/payment/pom.xml b/payment/pom.xml
index cb39ebf..e4933be 100644
--- a/payment/pom.xml
+++ b/payment/pom.xml
@@ -26,19 +26,23 @@
         </dependency>
         <dependency>
             <groupId>com.ning.billing</groupId>
-            <artifactId>killbill-invoice</artifactId>
+            <artifactId>killbill-util</artifactId>
         </dependency>
         <dependency>
             <groupId>com.ning.billing</groupId>
-            <artifactId>killbill-account</artifactId>
+            <artifactId>killbill-junction</artifactId>
+            <scope>test</scope>
         </dependency>
         <dependency>
             <groupId>com.ning.billing</groupId>
-            <artifactId>killbill-util</artifactId>
+            <artifactId>killbill-account</artifactId>
+            <type>test-jar</type>
+            <scope>test</scope>
         </dependency>
         <dependency>
             <groupId>com.ning.billing</groupId>
-            <artifactId>killbill-junction</artifactId>
+            <artifactId>killbill-invoice</artifactId>
+            <type>test-jar</type>
             <scope>test</scope>
         </dependency>
         <dependency>
@@ -78,12 +82,13 @@
             <artifactId>slf4j-api</artifactId>
         </dependency>
 
+        <!--  TEST SCOPE -->
         <dependency>
             <groupId>org.testng</groupId>
             <artifactId>testng</artifactId>
             <scope>test</scope>
         </dependency>
-        <dependency>
+        <dependency> 
             <groupId>com.jayway.awaitility</groupId>
             <artifactId>awaitility</artifactId>
             <scope>test</scope>
@@ -94,18 +99,7 @@
             <type>test-jar</type>
             <scope>test</scope>
         </dependency>
-        <dependency>
-            <groupId>com.ning.billing</groupId>
-            <artifactId>killbill-account</artifactId>
-            <type>test-jar</type>
-            <scope>test</scope>
-        </dependency>
-        <dependency>
-            <groupId>com.ning.billing</groupId>
-            <artifactId>killbill-invoice</artifactId>
-            <type>test-jar</type>
-            <scope>test</scope>
-        </dependency>
+
         <dependency>
             <groupId>mysql</groupId>
             <artifactId>mysql-connector-mxj</artifactId>
diff --git a/payment/src/main/java/com/ning/billing/payment/api/DefaultPaymentApi.java b/payment/src/main/java/com/ning/billing/payment/api/DefaultPaymentApi.java
index 552fc3e..46b43e0 100644
--- a/payment/src/main/java/com/ning/billing/payment/api/DefaultPaymentApi.java
+++ b/payment/src/main/java/com/ning/billing/payment/api/DefaultPaymentApi.java
@@ -18,24 +18,23 @@ package com.ning.billing.payment.api;
 
 import java.math.BigDecimal;
 import java.util.ArrayList;
+import java.util.LinkedList;
 import java.util.List;
 import java.util.UUID;
 
-import javax.annotation.Nullable;
-
 import org.apache.commons.lang.StringUtils;
 import org.joda.time.DateTime;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import com.google.inject.Inject;
+import com.ning.billing.ErrorCode;
 import com.ning.billing.account.api.Account;
 import com.ning.billing.account.api.AccountApiException;
 import com.ning.billing.account.api.AccountUserApi;
 import com.ning.billing.config.PaymentConfig;
 import com.ning.billing.invoice.api.Invoice;
 import com.ning.billing.invoice.api.InvoicePaymentApi;
-import com.ning.billing.invoice.model.DefaultInvoicePayment;
 import com.ning.billing.payment.RetryService;
 import com.ning.billing.payment.dao.PaymentDao;
 import com.ning.billing.payment.provider.PaymentProviderPlugin;
@@ -69,9 +68,14 @@ public class DefaultPaymentApi implements PaymentApi {
     }
 
     @Override
-    public Either<PaymentErrorEvent, PaymentMethodInfo> getPaymentMethod(@Nullable String accountKey, String paymentMethodId) {
+    public PaymentMethodInfo getPaymentMethod(final String accountKey, final String paymentMethodId) 
+    throws PaymentApiException {
         final PaymentProviderPlugin plugin = getPaymentProviderPlugin(accountKey);
-        return plugin.getPaymentMethodInfo(paymentMethodId);
+        Either<PaymentErrorEvent, PaymentMethodInfo> result = plugin.getPaymentMethodInfo(paymentMethodId);
+        if (result.isLeft()) {
+            throw new PaymentApiException(ErrorCode.PAYMENT_NO_SUCH_PAYMENT_METHOD, accountKey, paymentMethodId);
+        }
+        return result.getRight();
     }
 
     private PaymentProviderPlugin getPaymentProviderPlugin(String accountKey) {
@@ -101,60 +105,87 @@ public class DefaultPaymentApi implements PaymentApi {
     }
 
     @Override
-    public Either<PaymentErrorEvent, List<PaymentMethodInfo>> getPaymentMethods(String accountKey) {
+    public List<PaymentMethodInfo> getPaymentMethods(String accountKey)
+    throws PaymentApiException {
         final PaymentProviderPlugin plugin = getPaymentProviderPlugin(accountKey);
-        return plugin.getPaymentMethods(accountKey);
+        Either<PaymentErrorEvent, List<PaymentMethodInfo>> result =  plugin.getPaymentMethods(accountKey);
+        if (result.isLeft()) {
+            throw new PaymentApiException(ErrorCode.PAYMENT_NO_PAYMENT_METHODS, accountKey);
+        }
+        return result.getRight();
     }
 
     @Override
-    public Either<PaymentErrorEvent, Void> updatePaymentGateway(String accountKey, CallContext context) {
+    public void updatePaymentGateway(String accountKey, CallContext context) 
+    throws PaymentApiException {
         final PaymentProviderPlugin plugin = getPaymentProviderPlugin(accountKey);
-        return plugin.updatePaymentGateway(accountKey);
+        Either<PaymentErrorEvent, Void> result =  plugin.updatePaymentGateway(accountKey);
+        if (result.isLeft()) {
+            throw new PaymentApiException(ErrorCode.PAYMENT_UPD_GATEWAY_FAILED, accountKey, result.getLeft().getMessage());
+        }
+        return;
     }
 
     @Override
-    public Either<PaymentErrorEvent, PaymentProviderAccount> getPaymentProviderAccount(String accountKey) {
+    public PaymentProviderAccount getPaymentProviderAccount(String accountKey)
+    throws PaymentApiException {
         final PaymentProviderPlugin plugin = getPaymentProviderPlugin(accountKey);
-        return plugin.getPaymentProviderAccount(accountKey);
+        Either<PaymentErrorEvent, PaymentProviderAccount> result = plugin.getPaymentProviderAccount(accountKey);
+        if (result.isLeft()) {
+            throw new PaymentApiException(ErrorCode.PAYMENT_GET_PAYMENT_PROVIDER, accountKey, result.getLeft().getMessage());
+        }
+        return result.getRight();
     }
 
     @Override
-    public Either<PaymentErrorEvent, String> addPaymentMethod(@Nullable String accountKey, PaymentMethodInfo paymentMethod, CallContext context) {
+    public String addPaymentMethod(String accountKey, PaymentMethodInfo paymentMethod, CallContext context) 
+    throws PaymentApiException {
         final PaymentProviderPlugin plugin = getPaymentProviderPlugin(accountKey);
-        return plugin.addPaymentMethod(accountKey, paymentMethod);
+        Either<PaymentErrorEvent, String> result =  plugin.addPaymentMethod(accountKey, paymentMethod);
+        if (result.isLeft()) {
+            throw new PaymentApiException(ErrorCode.PAYMENT_ADD_PAYMENT_METHOD, accountKey, result.getLeft().getMessage());
+        }
+        return result.getRight();
     }
 
+
     @Override
-    public Either<PaymentErrorEvent, Void> deletePaymentMethod(String accountKey, String paymentMethodId, CallContext context) {
+    public void deletePaymentMethod(String accountKey, String paymentMethodId, CallContext context) 
+    throws PaymentApiException {
         final PaymentProviderPlugin plugin = getPaymentProviderPlugin(accountKey);
-        return plugin.deletePaymentMethod(accountKey, paymentMethodId);
+        Either<PaymentErrorEvent, Void> result =  plugin.deletePaymentMethod(accountKey, paymentMethodId);
+        if (result.isLeft()) {
+            throw new PaymentApiException(ErrorCode.PAYMENT_DEL_PAYMENT_METHOD, accountKey, result.getLeft().getMessage());
+        }
+        return;
     }
 
     @Override
-    public Either<PaymentErrorEvent, PaymentMethodInfo> updatePaymentMethod(String accountKey, PaymentMethodInfo paymentMethodInfo, CallContext context) {
+    public PaymentMethodInfo updatePaymentMethod(String accountKey, PaymentMethodInfo paymentMethodInfo, CallContext context) 
+    throws PaymentApiException {
         final PaymentProviderPlugin plugin = getPaymentProviderPlugin(accountKey);
-        return plugin.updatePaymentMethod(accountKey, paymentMethodInfo);
+        Either<PaymentErrorEvent, PaymentMethodInfo> result = plugin.updatePaymentMethod(accountKey, paymentMethodInfo);
+        if (result.isLeft()) {
+            throw new PaymentApiException(ErrorCode.PAYMENT_UPD_PAYMENT_METHOD, accountKey, result.getLeft().getMessage());
+        }
+        return result.getRight();
     }
 
     @Override
-    public List<Either<PaymentErrorEvent, PaymentInfoEvent>> createPayment(String accountKey, List<String> invoiceIds, CallContext context) {
+    public List<PaymentInfoEvent> createPayment(String accountKey, List<String> invoiceIds, CallContext context) 
+    throws PaymentApiException {
         try {
             final Account account = accountUserApi.getAccountByKey(accountKey);
             return createPayment(account, invoiceIds, context);
         } catch (AccountApiException e) {
-            log.error("Error getting payment provider plugin.", e);
-            List<Either<PaymentErrorEvent, PaymentInfoEvent>> result = new ArrayList<Either<PaymentErrorEvent, PaymentInfoEvent>>();
-            result.add(new Either.Left<PaymentErrorEvent, PaymentInfoEvent>((PaymentErrorEvent) new DefaultPaymentErrorEvent("createPaymentError", e.getMessage(),
-                    null,
-                    null,
-                    context.getUserToken())));
-            return result;
+            throw new PaymentApiException(e);
         }
-
     }
 
     @Override
-    public Either<PaymentErrorEvent, PaymentInfoEvent> createPaymentForPaymentAttempt(UUID paymentAttemptId, CallContext context) {
+    public PaymentInfoEvent createPaymentForPaymentAttempt(UUID paymentAttemptId, CallContext context) 
+    throws PaymentApiException {
+
         PaymentAttempt paymentAttempt = paymentDao.getPaymentAttemptById(paymentAttemptId);
         if (paymentAttempt != null) {
             try {
@@ -165,40 +196,35 @@ public class DefaultPaymentApi implements PaymentApi {
                     if (invoice.getBalance().compareTo(BigDecimal.ZERO) <= 0 ) {
                         // TODO: send a notification that invoice was ignored?
                         log.info("Received invoice for payment with outstanding amount of 0 {} ", invoice);
-                        return Either.left((PaymentErrorEvent) new DefaultPaymentErrorEvent("invoice_balance_0",
-                                "Invoice balance was 0 or less",
-                                paymentAttempt.getAccountId(),
-                                paymentAttempt.getInvoiceId(),
-                                context.getUserToken()));
-                    }
-                    else {
+                        throw new PaymentApiException(ErrorCode.PAYMENT_CREATE_PAYMENT_FOR_ATTEMPT_WITH_NON_POSITIVE_INV, account.getId());
+
+                    } else {
+                        
                         PaymentAttempt newPaymentAttempt = new DefaultPaymentAttempt.Builder(paymentAttempt)
-                        .setRetryCount(paymentAttempt.getRetryCount() + 1)
-                        .setPaymentAttemptId(UUID.randomUUID())
-                        .build();
+                                                                                    .setRetryCount(paymentAttempt.getRetryCount() + 1)
+                                                                                    .setPaymentAttemptId(UUID.randomUUID())
+                                                                                    .build();
 
                         paymentDao.createPaymentAttempt(newPaymentAttempt, context);
-                        return processPayment(getPaymentProviderPlugin(account), account, invoice, newPaymentAttempt, context);
+                        Either<PaymentErrorEvent, PaymentInfoEvent> result =  processPayment(getPaymentProviderPlugin(account), account, invoice, newPaymentAttempt, context);
+                        if (result.isLeft()) {
+                            throw new PaymentApiException(ErrorCode.PAYMENT_CREATE_PAYMENT_FOR_ATTEMPT, account.getId(),  paymentAttemptId, result.getLeft().getMessage());                            
+                        }
+                        return result.getRight();
+
                     }
                 }
             } catch (AccountApiException e) {
-                log.error("Error creating payment attempt.", e);
-                return new Either.Left<PaymentErrorEvent, PaymentInfoEvent>((PaymentErrorEvent) new DefaultPaymentErrorEvent("createPaymentError", e.getMessage(),
-                        null,
-                        null,
-                        context.getUserToken()));
-
+                throw new PaymentApiException(e);
             }
         }
-        return Either.left((PaymentErrorEvent) new DefaultPaymentErrorEvent("retry_payment_error",
-                "Could not load payment attempt, invoice or account for id " + paymentAttemptId,
-                paymentAttempt.getAccountId(),
-                paymentAttempt.getInvoiceId(),
-                context.getUserToken()));
+        throw new PaymentApiException(ErrorCode.PAYMENT_CREATE_PAYMENT_FOR_ATTEMPT_BAD, paymentAttemptId);
     }
 
     @Override
-    public List<Either<PaymentErrorEvent, PaymentInfoEvent>> createPayment(Account account, List<String> invoiceIds, CallContext context) {
+    public List<PaymentInfoEvent> createPayment(Account account, List<String> invoiceIds, CallContext context) 
+        throws PaymentApiException {
+        
         final PaymentProviderPlugin plugin = getPaymentProviderPlugin(account);
 
         List<Either<PaymentErrorEvent, PaymentInfoEvent>> processedPaymentsOrErrors = new ArrayList<Either<PaymentErrorEvent, PaymentInfoEvent>>(invoiceIds.size());
@@ -225,7 +251,15 @@ public class DefaultPaymentApi implements PaymentApi {
             }
         }
 
-        return processedPaymentsOrErrors;
+        List<Either<PaymentErrorEvent, PaymentInfoEvent>> result =  processedPaymentsOrErrors;
+        List<PaymentInfoEvent> info = new LinkedList<PaymentInfoEvent>();
+        for (Either<PaymentErrorEvent, PaymentInfoEvent> cur : result) {
+            if (cur.isLeft()) {
+                throw new PaymentApiException(ErrorCode.PAYMENT_CREATE_PAYMENT, account.getId(), cur.getLeft().getMessage());
+            }
+            info.add(cur.getRight());
+        }
+        return info;
     }
 
     private Either<PaymentErrorEvent, PaymentInfoEvent> processPayment(PaymentProviderPlugin plugin, Account account, Invoice invoice,
@@ -252,28 +286,29 @@ public class DefaultPaymentApi implements PaymentApi {
 
                 if (paymentMethodInfo instanceof CreditCardPaymentMethodInfo) {
                     CreditCardPaymentMethodInfo ccPaymentMethod = (CreditCardPaymentMethodInfo)paymentMethodInfo;
-                    paymentDao.updatePaymentInfo(ccPaymentMethod.getType(), paymentInfo.getPaymentId(), ccPaymentMethod.getCardType(), ccPaymentMethod.getCardCountry(), context);
+                    paymentDao.updatePaymentInfo(ccPaymentMethod.getType(), paymentInfo.getId(), ccPaymentMethod.getCardType(), ccPaymentMethod.getCardCountry(), context);
                 }
                 else if (paymentMethodInfo instanceof PaypalPaymentMethodInfo) {
                     PaypalPaymentMethodInfo paypalPaymentMethodInfo = (PaypalPaymentMethodInfo)paymentMethodInfo;
-                    paymentDao.updatePaymentInfo(paypalPaymentMethodInfo.getType(), paymentInfo.getPaymentId(), null, null, context);
+                    paymentDao.updatePaymentInfo(paypalPaymentMethodInfo.getType(), paymentInfo.getId(), null, null, context);
                 }
             } else {
                 log.info(paymentMethodInfoOrError.getLeft().getMessage());
             }
 
-            if (paymentInfo.getPaymentId() != null) {
-                paymentDao.updatePaymentAttemptWithPaymentId(paymentAttempt.getId(), paymentInfo.getPaymentId(), context);
+            if (paymentInfo.getId() != null) {
+                paymentDao.updatePaymentAttemptWithPaymentId(paymentAttempt.getId(), paymentInfo.getId(), context);
             }
         }
 
-        invoicePaymentApi.notifyOfPaymentAttempt(new DefaultInvoicePayment(paymentAttempt.getId(),
+        invoicePaymentApi.notifyOfPaymentAttempt(
                 invoice.getId(),
-                paymentAttempt.getPaymentAttemptDate(),
                 paymentInfo == null || paymentInfo.getStatus().equalsIgnoreCase("Error") ? null : paymentInfo.getAmount(),
                         //                                                                         paymentInfo.getRefundAmount(), TODO
-                        paymentInfo == null || paymentInfo.getStatus().equalsIgnoreCase("Error") ? null : invoice.getCurrency()),
-                        context);
+                        paymentInfo == null || paymentInfo.getStatus().equalsIgnoreCase("Error") ? null : invoice.getCurrency(),
+                                paymentAttempt.getId(),
+                                paymentAttempt.getPaymentAttemptDate(),
+                                context);
 
         return paymentOrError;
     }
@@ -310,38 +345,51 @@ public class DefaultPaymentApi implements PaymentApi {
     }
 
     @Override
-    public Either<PaymentErrorEvent, String> createPaymentProviderAccount(Account account, CallContext context) {
+    public String createPaymentProviderAccount(Account account, CallContext context) 
+        throws PaymentApiException {
         final PaymentProviderPlugin plugin = getPaymentProviderPlugin((Account)null);
-        return plugin.createPaymentProviderAccount(account);
+        Either<PaymentErrorEvent, String> result =  plugin.createPaymentProviderAccount(account);
+        if (result.isLeft()) {
+            throw new PaymentApiException(ErrorCode.PAYMENT_CREATE_PAYMENT_PROVIDER_ACCOUNT, account.getId(), result.getLeft().getMessage());
+        }
+        return result.getRight();
     }
 
     @Override
-    public Either<PaymentErrorEvent, Void> updatePaymentProviderAccountContact(String externalKey, CallContext context) {
+    public void updatePaymentProviderAccountContact(String externalKey, CallContext context) 
+        throws PaymentApiException {
         try {
             Account account = accountUserApi.getAccountByKey(externalKey);
             final PaymentProviderPlugin plugin = getPaymentProviderPlugin(account);
-            return plugin.updatePaymentProviderAccountExistingContact(account);
+            Either<PaymentErrorEvent, Void> result = plugin.updatePaymentProviderAccountExistingContact(account);
+            if (result.isLeft()) {
+                throw new PaymentApiException(ErrorCode.PAYMENT_UPD_PAYMENT_PROVIDER_ACCOUNT, account.getId(), result.getLeft().getMessage());
+            }
+            return;
         } catch (AccountApiException e) {
-            log.error("Error updating payment provider account contact.", e);
-            return new Either.Left<PaymentErrorEvent, Void>((PaymentErrorEvent) new DefaultPaymentErrorEvent("updatePaymentProviderAccountContactError", e.getMessage(),
-                    null,
-                    null,
-                    context.getUserToken()));
-
+            throw new PaymentApiException(e);
         }
     }
 
     @Override
-    public PaymentAttempt getPaymentAttemptForPaymentId(String id) {
+    public PaymentAttempt getPaymentAttemptForPaymentId(UUID id) {
         return paymentDao.getPaymentAttemptForPaymentId(id);
     }
 
     @Override
-    public List<Either<PaymentErrorEvent, PaymentInfoEvent>> createRefund(Account account,
-            List<String> invoiceIds,
-            CallContext context) {
+    public List<PaymentInfoEvent> createRefund(Account account, List<String> invoiceIds, CallContext context)
+        throws PaymentApiException {
+            
         final PaymentProviderPlugin plugin = getPaymentProviderPlugin(account);
-        return plugin.processRefund(account);
+        List<Either<PaymentErrorEvent, PaymentInfoEvent>> result = plugin.processRefund(account);
+        List<PaymentInfoEvent> info =  new LinkedList<PaymentInfoEvent>();
+        for (Either<PaymentErrorEvent, PaymentInfoEvent> cur : result) {
+            if (cur.isLeft()) {
+                throw new PaymentApiException(ErrorCode.PAYMENT_CREATE_REFUND, account.getId(), cur.getLeft().getMessage());
+            }
+            info.add(cur.getRight());
+        }
+        return info;
     }
 
     @Override
diff --git a/payment/src/main/java/com/ning/billing/payment/api/DefaultPaymentAttempt.java b/payment/src/main/java/com/ning/billing/payment/api/DefaultPaymentAttempt.java
index b523af2..681f0ca 100644
--- a/payment/src/main/java/com/ning/billing/payment/api/DefaultPaymentAttempt.java
+++ b/payment/src/main/java/com/ning/billing/payment/api/DefaultPaymentAttempt.java
@@ -32,10 +32,12 @@ public class DefaultPaymentAttempt extends EntityBase implements PaymentAttempt 
     private final UUID accountId;
     private final BigDecimal amount;
     private final Currency currency;
-    private final String paymentId;
+    private final UUID paymentId;
     private final DateTime invoiceDate;
     private final DateTime paymentAttemptDate;
     private final Integer retryCount;
+    private final DateTime createdDate;
+    private final DateTime updatedDate;
 
     public DefaultPaymentAttempt(UUID id,
                           UUID invoiceId,
@@ -44,8 +46,9 @@ public class DefaultPaymentAttempt extends EntityBase implements PaymentAttempt 
                           Currency currency,
                           DateTime invoiceDate,
                           DateTime paymentAttemptDate,
-                          String paymentId,
-                          Integer retryCount) {
+                          UUID paymentId,
+                          Integer retryCount,
+                          DateTime createdDate, DateTime updatedDate) {
         super(id);
         this.invoiceId = invoiceId;
         this.accountId = accountId;
@@ -55,18 +58,20 @@ public class DefaultPaymentAttempt extends EntityBase implements PaymentAttempt 
         this.paymentAttemptDate = paymentAttemptDate == null ? new DateTime(DateTimeZone.UTC) : paymentAttemptDate;
         this.paymentId = paymentId;
         this.retryCount = retryCount == null ? 0 : retryCount;
+        this.createdDate = createdDate;
+        this.updatedDate = updatedDate;
     }
 
     public DefaultPaymentAttempt(UUID paymentAttemptId, UUID invoiceId, UUID accountId, BigDecimal amount, Currency currency, DateTime invoiceDate, DateTime paymentAttemptDate) {
-        this(paymentAttemptId, invoiceId, accountId, amount, currency, invoiceDate, paymentAttemptDate, null, null);
+        this(paymentAttemptId, invoiceId, accountId, amount, currency, invoiceDate, paymentAttemptDate, null, null, null, null);
     }
 
     public DefaultPaymentAttempt(UUID paymentAttemptId, UUID invoiceId, UUID accountId, DateTime invoiceDate, DateTime paymentAttemptDate) {
-        this(paymentAttemptId, invoiceId, accountId, null, null, invoiceDate, paymentAttemptDate, null, null);
+        this(paymentAttemptId, invoiceId, accountId, null, null, invoiceDate, paymentAttemptDate, null, null, null, null);
     }
 
     public DefaultPaymentAttempt(UUID paymentAttemptId, Invoice invoice) {
-        this(paymentAttemptId, invoice.getId(), invoice.getAccountId(), invoice.getBalance(), invoice.getCurrency(), invoice.getInvoiceDate(), null, null, null);
+        this(paymentAttemptId, invoice.getId(), invoice.getAccountId(), invoice.getBalance(), invoice.getCurrency(), invoice.getInvoiceDate(), null, null, null, null, null);
     }
 
     @Override public DateTime getInvoiceDate() {
@@ -77,7 +82,7 @@ public class DefaultPaymentAttempt extends EntityBase implements PaymentAttempt 
         return id;
     }
 
-    @Override public String getPaymentId() {
+    @Override public UUID getPaymentId() {
         return paymentId;
     }
 
@@ -105,6 +110,14 @@ public class DefaultPaymentAttempt extends EntityBase implements PaymentAttempt 
         return retryCount;
     }
 
+    @Override public DateTime getCreatedDate() {
+        return createdDate;
+    }
+
+    @Override public DateTime getUpdatedDate() {
+        return updatedDate;
+    }
+
     @Override
     public String toString() {
         return "PaymentAttempt [paymentAttemptId=" + id + ", invoiceId=" + invoiceId + ", accountId=" + accountId + ", amount=" + amount + ", currency=" + currency + ", paymentId=" + paymentId + ", invoiceDate=" + invoiceDate + ", paymentAttemptDate=" + paymentAttemptDate + ", retryCount=" + retryCount + "]";
@@ -124,7 +137,8 @@ public class DefaultPaymentAttempt extends EntityBase implements PaymentAttempt 
                                 invoiceDate,
                                 paymentAttemptDate,
                                 paymentId,
-                                retryCount);
+                                retryCount,
+                                createdDate, updatedDate);
     }
 
     @Override
@@ -144,11 +158,13 @@ public class DefaultPaymentAttempt extends EntityBase implements PaymentAttempt 
             return false;
         if (paymentId != null ? !paymentId.equals(that.getPaymentId()) : that.getPaymentId() != null) return false;
         if (retryCount != null ? !retryCount.equals(that.getRetryCount()) : that.getRetryCount() != null) return false;
+        if (createdDate.compareTo(that.getCreatedDate()) != 0) return false;
+        if (updatedDate.compareTo(that.getUpdatedDate()) != 0) return false;
 
         return true;
     }
 
-        public static class Builder {
+    public static class Builder {
         private UUID id;
         private UUID invoiceId;
         private UUID accountId;
@@ -156,8 +172,10 @@ public class DefaultPaymentAttempt extends EntityBase implements PaymentAttempt 
         private Currency currency;
         private DateTime invoiceDate;
         private DateTime paymentAttemptDate;
-        private String paymentId;
+        private UUID paymentId;
         private Integer retryCount;
+        private DateTime createdDate;
+        private DateTime updatedDate;
 
         public Builder() {
         }
@@ -172,6 +190,8 @@ public class DefaultPaymentAttempt extends EntityBase implements PaymentAttempt 
             this.paymentAttemptDate = src.getPaymentAttemptDate();
             this.paymentId = src.getPaymentId();
             this.retryCount = src.getRetryCount();
+            this.createdDate = src.getCreatedDate();
+            this.updatedDate = src.getUpdatedDate();
         }
 
         public Builder setPaymentAttemptId(UUID paymentAttemptId) {
@@ -209,7 +229,7 @@ public class DefaultPaymentAttempt extends EntityBase implements PaymentAttempt 
             return this;
         }
 
-        public Builder setPaymentId(String paymentId) {
+        public Builder setPaymentId(UUID paymentId) {
             this.paymentId = paymentId;
             return this;
         }
@@ -219,6 +239,16 @@ public class DefaultPaymentAttempt extends EntityBase implements PaymentAttempt 
             return this;
         }
 
+        public Builder setCreatedDate(DateTime createdDate) {
+            this.createdDate = createdDate;
+            return this;
+        }
+
+        public Builder setUpdateDate(DateTime updateDate) {
+            this.updatedDate = updateDate;
+            return this;
+        }
+
         public PaymentAttempt build() {
             return new DefaultPaymentAttempt(id,
                                       invoiceId,
@@ -228,8 +258,8 @@ public class DefaultPaymentAttempt extends EntityBase implements PaymentAttempt 
                                       invoiceDate,
                                       paymentAttemptDate,
                                       paymentId,
-                                      retryCount);
+                                      retryCount,
+                                      createdDate, updatedDate);
         }
-
     }
 }
diff --git a/payment/src/main/java/com/ning/billing/payment/api/DefaultPaymentInfoEvent.java b/payment/src/main/java/com/ning/billing/payment/api/DefaultPaymentInfoEvent.java
index 00eedd7..02b7299 100644
--- a/payment/src/main/java/com/ning/billing/payment/api/DefaultPaymentInfoEvent.java
+++ b/payment/src/main/java/com/ning/billing/payment/api/DefaultPaymentInfoEvent.java
@@ -19,6 +19,7 @@ package com.ning.billing.payment.api;
 import java.math.BigDecimal;
 import java.util.UUID;
 
+import com.ning.billing.util.entity.EntityBase;
 import org.codehaus.jackson.annotate.JsonCreator;
 import org.codehaus.jackson.annotate.JsonIgnore;
 import org.codehaus.jackson.annotate.JsonProperty;
@@ -26,8 +27,7 @@ import org.joda.time.DateTime;
 
 import com.google.common.base.Objects;
 
-public class DefaultPaymentInfoEvent implements PaymentInfoEvent {
-    private final String paymentId;
+public class DefaultPaymentInfoEvent extends EntityBase implements PaymentInfoEvent {
     private final BigDecimal amount;
     private final BigDecimal refundAmount;
     private final String paymentNumber;
@@ -43,7 +43,7 @@ public class DefaultPaymentInfoEvent implements PaymentInfoEvent {
     private final DateTime effectiveDate;
 
     @JsonCreator
-    public DefaultPaymentInfoEvent(@JsonProperty("paymentId") String paymentId,
+    public DefaultPaymentInfoEvent(@JsonProperty("id") UUID id,
                        @JsonProperty("amount") BigDecimal amount,
                        @JsonProperty("refundAmount") BigDecimal refundAmount,
                        @JsonProperty("bankIdentificationNumber") String bankIdentificationNumber,
@@ -57,7 +57,7 @@ public class DefaultPaymentInfoEvent implements PaymentInfoEvent {
                        @JsonProperty("cardCountry") String cardCountry,
                        @JsonProperty("userToken") UUID userToken,
                        @JsonProperty("effectiveDate") DateTime effectiveDate) {
-        this.paymentId = paymentId;
+        super(id);
         this.amount = amount;
         this.refundAmount = refundAmount;
         this.bankIdentificationNumber = bankIdentificationNumber;
@@ -74,7 +74,7 @@ public class DefaultPaymentInfoEvent implements PaymentInfoEvent {
     }
 
     public DefaultPaymentInfoEvent(DefaultPaymentInfoEvent src) {
-        this(src.paymentId,
+        this(src.id,
              src.amount,
              src.refundAmount,
              src.bankIdentificationNumber,
@@ -106,11 +106,6 @@ public class DefaultPaymentInfoEvent implements PaymentInfoEvent {
     }
 
     @Override
-    public String getPaymentId() {
-        return paymentId;
-    }
-
-    @Override
     public BigDecimal getAmount() {
         return amount;
     }
@@ -170,13 +165,8 @@ public class DefaultPaymentInfoEvent implements PaymentInfoEvent {
         return type;
     }
 
-    @Override
-    public UUID getId() {
-        return UUID.fromString(paymentId);
-    }
-
     public static class Builder {
-        private String paymentId;
+        private UUID id;
         private BigDecimal amount;
         private BigDecimal refundAmount;
         private String paymentNumber;
@@ -195,7 +185,7 @@ public class DefaultPaymentInfoEvent implements PaymentInfoEvent {
         }
 
         public Builder(DefaultPaymentInfoEvent src) {
-            this.paymentId = src.paymentId;
+            this.id = src.id;
             this.amount = src.amount;
             this.refundAmount = src.refundAmount;
             this.paymentNumber = src.paymentNumber;
@@ -211,8 +201,8 @@ public class DefaultPaymentInfoEvent implements PaymentInfoEvent {
             this.userToken = src.userToken;
         }
 
-        public Builder setPaymentId(String paymentId) {
-            this.paymentId = paymentId;
+        public Builder setId(UUID id) {
+            this.id = id;
             return this;
         }
 
@@ -282,7 +272,7 @@ public class DefaultPaymentInfoEvent implements PaymentInfoEvent {
         }
 
         public PaymentInfoEvent build() {
-            return new DefaultPaymentInfoEvent(paymentId,
+            return new DefaultPaymentInfoEvent(id,
                                    amount,
                                    refundAmount,
                                    bankIdentificationNumber,
@@ -301,7 +291,7 @@ public class DefaultPaymentInfoEvent implements PaymentInfoEvent {
 
     @Override
     public int hashCode() {
-        return Objects.hashCode(paymentId,
+        return Objects.hashCode(id,
                                 amount,
                                 refundAmount,
                                 bankIdentificationNumber,
@@ -329,7 +319,7 @@ public class DefaultPaymentInfoEvent implements PaymentInfoEvent {
         if (cardCountry != null ? !cardCountry.equals(that.cardCountry) : that.cardCountry != null) return false;
         if (cardType != null ? !cardType.equals(that.cardType) : that.cardType != null) return false;
         if (effectiveDate == null ? that.effectiveDate != null : effectiveDate.compareTo(that.effectiveDate) != 0) return false;
-        if (paymentId != null ? !paymentId.equals(that.paymentId) : that.paymentId != null) return false;
+        if (id != null ? !id.equals(that.id) : that.id != null) return false;
         if (paymentMethod != null ? !paymentMethod.equals(that.paymentMethod) : that.paymentMethod != null)
             return false;
         if (paymentMethodId != null ? !paymentMethodId.equals(that.paymentMethodId) : that.paymentMethodId != null)
@@ -346,6 +336,6 @@ public class DefaultPaymentInfoEvent implements PaymentInfoEvent {
 
     @Override
     public String toString() {
-        return "PaymentInfo [paymentId=" + paymentId + ", amount=" + amount + ", refundAmount=" + refundAmount + ", paymentNumber=" + paymentNumber + ", bankIdentificationNumber=" + bankIdentificationNumber + ", status=" + status + ", type=" + type + ", referenceId=" + referenceId + ", paymentMethodId=" + paymentMethodId + ", paymentMethod=" + paymentMethod + ", cardType=" + cardType + ", cardCountry=" + cardCountry + ", effectiveDate=" + effectiveDate + "]";
+        return "PaymentInfo [id=" + id + ", amount=" + amount + ", refundAmount=" + refundAmount + ", paymentNumber=" + paymentNumber + ", bankIdentificationNumber=" + bankIdentificationNumber + ", status=" + status + ", type=" + type + ", referenceId=" + referenceId + ", paymentMethodId=" + paymentMethodId + ", paymentMethod=" + paymentMethod + ", cardType=" + cardType + ", cardCountry=" + cardCountry + ", effectiveDate=" + effectiveDate + "]";
     }
 }
diff --git a/payment/src/main/java/com/ning/billing/payment/dao/AuditedPaymentDao.java b/payment/src/main/java/com/ning/billing/payment/dao/AuditedPaymentDao.java
index cf39283..39201ac 100644
--- a/payment/src/main/java/com/ning/billing/payment/dao/AuditedPaymentDao.java
+++ b/payment/src/main/java/com/ning/billing/payment/dao/AuditedPaymentDao.java
@@ -46,8 +46,8 @@ public class AuditedPaymentDao implements PaymentDao {
     }
 
     @Override
-    public PaymentAttempt getPaymentAttemptForPaymentId(String paymentId) {
-        return paymentAttemptSqlDao.getPaymentAttemptForPaymentId(paymentId);
+    public PaymentAttempt getPaymentAttemptForPaymentId(UUID paymentId) {
+        return paymentAttemptSqlDao.getPaymentAttemptForPaymentId(paymentId.toString());
     }
 
     @Override
@@ -65,11 +65,11 @@ public class AuditedPaymentDao implements PaymentDao {
 
                 Long recordId = transactional.getRecordId(paymentAttempt.getId().toString());
                 EntityHistory<PaymentAttempt> history = new EntityHistory<PaymentAttempt>(paymentAttempt.getId(), recordId, paymentAttempt, ChangeType.INSERT);
-                transactional.addHistoryFromTransaction(history, context);
+                transactional.insertHistoryFromTransaction(history, context);
 
                 Long historyRecordId = transactional.getHistoryRecordId(recordId);
-                EntityAudit audit = new EntityAudit(historyRecordId, ChangeType.INSERT);
-                transactional.insertAuditFromTransaction(TableName.PAYMENT_ATTEMPTS, audit, context);
+                EntityAudit audit = new EntityAudit(TableName.PAYMENT_ATTEMPTS, historyRecordId, ChangeType.INSERT);
+                transactional.insertAuditFromTransaction(audit, context);
                 return savedPaymentAttempt;
             }
         });
@@ -85,11 +85,11 @@ public class AuditedPaymentDao implements PaymentDao {
 
                 Long recordId = transactional.getRecordId(paymentAttempt.getId().toString());
                 EntityHistory<PaymentAttempt> history = new EntityHistory<PaymentAttempt>(paymentAttempt.getId(), recordId, paymentAttempt, ChangeType.INSERT);
-                transactional.addHistoryFromTransaction(history, context);
+                transactional.insertHistoryFromTransaction(history, context);
 
                 Long historyRecordId = transactional.getHistoryRecordId(recordId);
-                EntityAudit audit = new EntityAudit(historyRecordId, ChangeType.INSERT);
-                transactional.insertAuditFromTransaction(TableName.PAYMENT_ATTEMPTS, audit, context);
+                EntityAudit audit = new EntityAudit(TableName.PAYMENT_ATTEMPTS, historyRecordId, ChangeType.INSERT);
+                transactional.insertAuditFromTransaction(audit, context);
 
                 return paymentAttempt;
             }
@@ -102,13 +102,13 @@ public class AuditedPaymentDao implements PaymentDao {
             @Override
             public Void inTransaction(PaymentSqlDao transactional, TransactionStatus status) throws Exception {
                 transactional.insertPaymentInfo(info, context);
-                Long recordId = transactional.getRecordId(info.getPaymentId());
+                Long recordId = transactional.getRecordId(info.getId().toString());
                 EntityHistory<PaymentInfoEvent> history = new EntityHistory<PaymentInfoEvent>(info.getId(), recordId, info, ChangeType.INSERT);
-                transactional.addHistoryFromTransaction(history, context);
+                transactional.insertHistoryFromTransaction(history, context);
 
                 Long historyRecordId = transactional.getHistoryRecordId(recordId);
-                EntityAudit audit = new EntityAudit(historyRecordId, ChangeType.INSERT);
-                transactional.insertAuditFromTransaction(TableName.PAYMENTS, audit, context);
+                EntityAudit audit = new EntityAudit(TableName.PAYMENTS, historyRecordId, ChangeType.INSERT);
+                transactional.insertAuditFromTransaction(audit, context);
 
                 return null;
             }
@@ -116,19 +116,19 @@ public class AuditedPaymentDao implements PaymentDao {
     }
 
     @Override
-    public void updatePaymentAttemptWithPaymentId(final UUID paymentAttemptId, final String paymentId, final CallContext context) {
+    public void updatePaymentAttemptWithPaymentId(final UUID paymentAttemptId, final UUID id, final CallContext context) {
         paymentAttemptSqlDao.inTransaction(new Transaction<Void, PaymentAttemptSqlDao>() {
             @Override
             public Void inTransaction(PaymentAttemptSqlDao transactional, TransactionStatus status) throws Exception {
-                transactional.updatePaymentAttemptWithPaymentId(paymentAttemptId.toString(), paymentId, context);
+                transactional.updatePaymentAttemptWithPaymentId(paymentAttemptId.toString(), id.toString(), context);
                 PaymentAttempt paymentAttempt = transactional.getPaymentAttemptById(paymentAttemptId.toString());
                 Long recordId = transactional.getRecordId(paymentAttemptId.toString());
                 EntityHistory<PaymentAttempt> history = new EntityHistory<PaymentAttempt>(paymentAttemptId, recordId, paymentAttempt, ChangeType.UPDATE);
-                transactional.addHistoryFromTransaction(history, context);
+                transactional.insertHistoryFromTransaction(history, context);
 
                 Long historyRecordId = transactional.getHistoryRecordId(recordId);
-                EntityAudit audit = new EntityAudit(historyRecordId, ChangeType.UPDATE);
-                transactional.insertAuditFromTransaction(TableName.PAYMENT_ATTEMPTS, audit, context);
+                EntityAudit audit = new EntityAudit(TableName.PAYMENT_ATTEMPTS, historyRecordId, ChangeType.UPDATE);
+                transactional.insertAuditFromTransaction(audit, context);
 
                 return null;
             }
@@ -136,21 +136,21 @@ public class AuditedPaymentDao implements PaymentDao {
     }
 
     @Override
-    public void updatePaymentInfo(final String type, final String paymentId, final String cardType,
+    public void updatePaymentInfo(final String type, final UUID paymentId, final String cardType,
                                   final String cardCountry, final CallContext context) {
         paymentSqlDao.inTransaction(new Transaction<Void, PaymentSqlDao>() {
             @Override
             public Void inTransaction(PaymentSqlDao transactional, TransactionStatus status) throws Exception {
-                transactional.updatePaymentInfo(type, paymentId, cardType, cardCountry, context);
-                PaymentInfoEvent paymentInfo = transactional.getPaymentInfo(paymentId);
+                transactional.updatePaymentInfo(type, paymentId.toString(), cardType, cardCountry, context);
+                PaymentInfoEvent paymentInfo = transactional.getPaymentInfo(paymentId.toString());
 
-                Long recordId = transactional.getRecordId(paymentId);
+                Long recordId = transactional.getRecordId(paymentId.toString());
                 EntityHistory<PaymentInfoEvent> history = new EntityHistory<PaymentInfoEvent>(paymentInfo.getId(), recordId, paymentInfo, ChangeType.UPDATE);
-                transactional.addHistoryFromTransaction(history, context);
+                transactional.insertHistoryFromTransaction(history, context);
 
                 Long historyRecordId = transactional.getHistoryRecordId(recordId);
-                EntityAudit audit = new EntityAudit(historyRecordId, ChangeType.UPDATE);
-                transactional.insertAuditFromTransaction(TableName.PAYMENT_HISTORY, audit, context);
+                EntityAudit audit = new EntityAudit(TableName.PAYMENT_HISTORY, historyRecordId, ChangeType.UPDATE);
+                transactional.insertAuditFromTransaction(audit, context);
 
                 return null;
             }
diff --git a/payment/src/main/java/com/ning/billing/payment/dao/PaymentAttemptHistoryBinder.java b/payment/src/main/java/com/ning/billing/payment/dao/PaymentAttemptHistoryBinder.java
new file mode 100644
index 0000000..e063655
--- /dev/null
+++ b/payment/src/main/java/com/ning/billing/payment/dao/PaymentAttemptHistoryBinder.java
@@ -0,0 +1,60 @@
+/*
+ * 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.payment.dao;
+
+import com.ning.billing.payment.api.PaymentAttempt;
+import com.ning.billing.util.dao.BinderBase;
+import com.ning.billing.util.dao.EntityHistory;
+import org.skife.jdbi.v2.SQLStatement;
+import org.skife.jdbi.v2.sqlobject.Binder;
+import org.skife.jdbi.v2.sqlobject.BinderFactory;
+import org.skife.jdbi.v2.sqlobject.BindingAnnotation;
+
+import java.lang.annotation.Annotation;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@BindingAnnotation(PaymentAttemptHistoryBinder.PaymentAttemptHistoryBinderFactory.class)
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.PARAMETER})
+public @interface PaymentAttemptHistoryBinder {
+    public static class PaymentAttemptHistoryBinderFactory extends BinderBase implements BinderFactory {
+        @Override
+        public Binder<PaymentAttemptHistoryBinder, EntityHistory<PaymentAttempt>> build(Annotation annotation) {
+            return new Binder<PaymentAttemptHistoryBinder, EntityHistory<PaymentAttempt>>() {
+                @Override
+                public void bind(SQLStatement q, PaymentAttemptHistoryBinder bind, EntityHistory<PaymentAttempt> history) {
+                    q.bind("recordId", history.getValue());
+                    q.bind("changeType", history.getChangeType().toString());
+
+                    PaymentAttempt paymentAttempt = history.getEntity();
+                    q.bind("id", paymentAttempt.getId().toString());
+                    q.bind("invoice_id", paymentAttempt.getInvoiceId().toString());
+                    q.bind("account_id", paymentAttempt.getAccountId().toString());
+                    q.bind("amount", paymentAttempt.getAmount());
+                    q.bind("currency", paymentAttempt.getCurrency().toString());
+                    q.bind("invoice_date", getDate(paymentAttempt.getInvoiceDate()));
+                    q.bind("payment_attempt_date", getDate(paymentAttempt.getPaymentAttemptDate()));
+                    q.bind("payment_id", paymentAttempt.getPaymentId().toString());
+                    q.bind("retry_count", paymentAttempt.getRetryCount());
+                }
+            };
+        }
+    }
+}
\ No newline at end of file
diff --git a/payment/src/main/java/com/ning/billing/payment/dao/PaymentAttemptSqlDao.java b/payment/src/main/java/com/ning/billing/payment/dao/PaymentAttemptSqlDao.java
index 1a5e220..747d513 100644
--- a/payment/src/main/java/com/ning/billing/payment/dao/PaymentAttemptSqlDao.java
+++ b/payment/src/main/java/com/ning/billing/payment/dao/PaymentAttemptSqlDao.java
@@ -21,12 +21,10 @@ import com.ning.billing.payment.api.DefaultPaymentAttempt;
 import com.ning.billing.payment.api.PaymentAttempt;
 import com.ning.billing.util.callcontext.CallContext;
 import com.ning.billing.util.callcontext.CallContextBinder;
-import com.ning.billing.util.dao.AuditSqlDao;
 import com.ning.billing.util.dao.BinderBase;
-import com.ning.billing.util.dao.HistorySqlDao;
+import com.ning.billing.util.dao.EntityHistory;
 import com.ning.billing.util.dao.MapperBase;
-import com.ning.billing.util.entity.dao.EntityDao;
-import com.ning.billing.util.entity.dao.EntitySqlDao;
+import com.ning.billing.util.entity.dao.UpdatableEntitySqlDao;
 import org.joda.time.DateTime;
 import org.skife.jdbi.v2.SQLStatement;
 import org.skife.jdbi.v2.StatementContext;
@@ -49,7 +47,7 @@ import java.util.UUID;
 
 @ExternalizedSqlViaStringTemplate3()
 @RegisterMapper(PaymentAttemptSqlDao.PaymentAttemptMapper.class)
-public interface PaymentAttemptSqlDao extends Transactional<PaymentAttemptSqlDao>, EntitySqlDao, HistorySqlDao<PaymentAttempt>, AuditSqlDao, CloseMe {
+public interface PaymentAttemptSqlDao extends Transactional<PaymentAttemptSqlDao>, UpdatableEntitySqlDao<PaymentAttempt>, CloseMe {
     @SqlUpdate
     void insertPaymentAttempt(@Bind(binder = PaymentAttemptBinder.class) PaymentAttempt paymentAttempt,
                               @CallContextBinder CallContext context);
@@ -58,7 +56,7 @@ public interface PaymentAttemptSqlDao extends Transactional<PaymentAttemptSqlDao
     PaymentAttempt getPaymentAttemptForPaymentId(@Bind("payment_id") String paymentId);
 
     @SqlQuery
-    PaymentAttempt getPaymentAttemptById(@Bind("payment_attempt_id") String paymentAttemptId);
+    PaymentAttempt getPaymentAttemptById(@Bind("id") String paymentAttemptId);
 
     @SqlQuery
     List<PaymentAttempt> getPaymentAttemptsForInvoiceId(@Bind("invoice_id") String invoiceId);
@@ -68,28 +66,35 @@ public interface PaymentAttemptSqlDao extends Transactional<PaymentAttemptSqlDao
 
 
     @SqlUpdate
-    void updatePaymentAttemptWithPaymentId(@Bind("payment_attempt_id") String paymentAttemptId,
+    void updatePaymentAttemptWithPaymentId(@Bind("id") String paymentAttemptId,
                                            @Bind("payment_id") String paymentId,
                                            @CallContextBinder CallContext context);
 
     @SqlUpdate
-    void updatePaymentAttemptWithRetryInfo(@Bind("payment_attempt_id") String paymentAttemptId,
+    void updatePaymentAttemptWithRetryInfo(@Bind("id") String paymentAttemptId,
                                            @Bind("retry_count") int retryCount,
                                            @CallContextBinder CallContext context);
+    
+    @Override
+    @SqlUpdate
+    public void insertHistoryFromTransaction(@PaymentAttemptHistoryBinder final EntityHistory<PaymentAttempt> account,
+                                            @CallContextBinder final CallContext context);
 
     public static class PaymentAttemptMapper extends MapperBase implements ResultSetMapper<PaymentAttempt> {
         @Override
         public PaymentAttempt map(int index, ResultSet rs, StatementContext ctx) throws SQLException {
 
-            UUID paymentAttemptId = UUID.fromString(rs.getString("id"));
-            UUID invoiceId = UUID.fromString(rs.getString("invoice_id"));
-            UUID accountId = UUID.fromString(rs.getString("account_id"));
+            UUID paymentAttemptId = getUUID(rs, "id");
+            UUID invoiceId = getUUID(rs, "invoice_id");
+            UUID accountId = getUUID(rs, "account_id");
             BigDecimal amount = rs.getBigDecimal("amount");
             Currency currency = Currency.valueOf(rs.getString("currency"));
             DateTime invoiceDate = getDate(rs, "invoice_date");
             DateTime paymentAttemptDate = getDate(rs, "payment_attempt_date");
-            String paymentId = rs.getString("payment_id");
+            UUID paymentId = getUUID(rs, "payment_id");
             Integer retryCount = rs.getInt("retry_count");
+            DateTime createdDate = getDate(rs, "created_date");
+            DateTime updatedDate = getDate(rs, "updated_date");
 
             return new DefaultPaymentAttempt(paymentAttemptId,
                                       invoiceId,
@@ -99,21 +104,22 @@ public interface PaymentAttemptSqlDao extends Transactional<PaymentAttemptSqlDao
                                       invoiceDate,
                                       paymentAttemptDate,
                                       paymentId,
-                                      retryCount);
+                                      retryCount,
+                                      createdDate, updatedDate);
         }
     }
 
     public static final class PaymentAttemptBinder extends BinderBase implements Binder<Bind, PaymentAttempt> {
         @Override
         public void bind(@SuppressWarnings("rawtypes") SQLStatement stmt, Bind bind, PaymentAttempt paymentAttempt) {
-            stmt.bind("id", paymentAttempt.getId() == null ? null : paymentAttempt.getId().toString());
+            stmt.bind("id", paymentAttempt.getId().toString());
             stmt.bind("invoice_id", paymentAttempt.getInvoiceId().toString());
             stmt.bind("account_id", paymentAttempt.getAccountId().toString());
             stmt.bind("amount", paymentAttempt.getAmount());
             stmt.bind("currency", paymentAttempt.getCurrency().toString());
             stmt.bind("invoice_date", getDate(paymentAttempt.getInvoiceDate()));
             stmt.bind("payment_attempt_date", getDate(paymentAttempt.getPaymentAttemptDate()));
-            stmt.bind("payment_id", paymentAttempt.getPaymentId());
+            stmt.bind("payment_id", paymentAttempt.getPaymentId().toString());
             stmt.bind("retry_count", paymentAttempt.getRetryCount());
         }
     }
diff --git a/payment/src/main/java/com/ning/billing/payment/dao/PaymentDao.java b/payment/src/main/java/com/ning/billing/payment/dao/PaymentDao.java
index 67c9027..581ed71 100644
--- a/payment/src/main/java/com/ning/billing/payment/dao/PaymentDao.java
+++ b/payment/src/main/java/com/ning/billing/payment/dao/PaymentDao.java
@@ -31,14 +31,14 @@ public interface PaymentDao {
 
     void savePaymentInfo(PaymentInfoEvent right, CallContext context);
 
-    PaymentAttempt getPaymentAttemptForPaymentId(String paymentId);
+    PaymentAttempt getPaymentAttemptForPaymentId(UUID paymentId);
     List<PaymentAttempt> getPaymentAttemptsForInvoiceIds(List<String> invoiceIds);
 
-    void updatePaymentAttemptWithPaymentId(UUID paymentAttemptId, String paymentId, CallContext context);
+    void updatePaymentAttemptWithPaymentId(UUID paymentAttemptId, UUID paymentId, CallContext context);
 
     List<PaymentAttempt> getPaymentAttemptsForInvoiceId(String invoiceId);
 
-    void updatePaymentInfo(String paymentMethodType, String paymentId, String cardType, String cardCountry, CallContext context);
+    void updatePaymentInfo(String paymentMethodType, UUID paymentId, String cardType, String cardCountry, CallContext context);
 
     List<PaymentInfoEvent> getPaymentInfoList(List<String> invoiceIds);
 
diff --git a/payment/src/main/java/com/ning/billing/payment/dao/PaymentHistoryBinder.java b/payment/src/main/java/com/ning/billing/payment/dao/PaymentHistoryBinder.java
new file mode 100644
index 0000000..2ec9fc7
--- /dev/null
+++ b/payment/src/main/java/com/ning/billing/payment/dao/PaymentHistoryBinder.java
@@ -0,0 +1,64 @@
+/*
+ * 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.payment.dao;
+
+import com.ning.billing.payment.api.PaymentInfoEvent;
+import com.ning.billing.util.dao.BinderBase;
+import com.ning.billing.util.dao.EntityHistory;
+import org.skife.jdbi.v2.SQLStatement;
+import org.skife.jdbi.v2.sqlobject.Binder;
+import org.skife.jdbi.v2.sqlobject.BinderFactory;
+import org.skife.jdbi.v2.sqlobject.BindingAnnotation;
+
+import java.lang.annotation.Annotation;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@BindingAnnotation(PaymentHistoryBinder.PaymentHistoryBinderFactory.class)
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.PARAMETER})
+public @interface PaymentHistoryBinder {
+    public static class PaymentHistoryBinderFactory extends BinderBase implements BinderFactory {
+        @Override
+        public Binder<PaymentHistoryBinder, EntityHistory<PaymentInfoEvent>> build(Annotation annotation) {
+            return new Binder<PaymentHistoryBinder, EntityHistory<PaymentInfoEvent>>() {
+                @Override
+                public void bind(SQLStatement q, PaymentHistoryBinder bind, EntityHistory<PaymentInfoEvent> history) {
+                    q.bind("recordId", history.getValue());
+                    q.bind("changeType", history.getChangeType().toString());
+
+                    PaymentInfoEvent paymentInfo = history.getEntity();
+                    q.bind("id", paymentInfo.getId().toString());
+                    q.bind("amount", paymentInfo.getAmount());
+                    q.bind("refund_amount", paymentInfo.getRefundAmount());
+                    q.bind("payment_number", paymentInfo.getPaymentNumber());
+                    q.bind("bank_identification_number", paymentInfo.getBankIdentificationNumber());
+                    q.bind("status", paymentInfo.getStatus());
+                    q.bind("payment_type", paymentInfo.getType());
+                    q.bind("reference_id", paymentInfo.getReferenceId());
+                    q.bind("payment_method_id", paymentInfo.getPaymentMethodId());
+                    q.bind("payment_method", paymentInfo.getPaymentMethod());
+                    q.bind("card_type", paymentInfo.getCardType());
+                    q.bind("card_country", paymentInfo.getCardCountry());
+                    q.bind("effective_date", getDate(paymentInfo.getEffectiveDate()));
+                }
+            };
+        }
+    }
+}
diff --git a/payment/src/main/java/com/ning/billing/payment/dao/PaymentSqlDao.java b/payment/src/main/java/com/ning/billing/payment/dao/PaymentSqlDao.java
index 0966829..bdd9829 100644
--- a/payment/src/main/java/com/ning/billing/payment/dao/PaymentSqlDao.java
+++ b/payment/src/main/java/com/ning/billing/payment/dao/PaymentSqlDao.java
@@ -24,12 +24,10 @@ import java.util.UUID;
 
 import com.ning.billing.util.callcontext.CallContext;
 import com.ning.billing.util.callcontext.CallContextBinder;
-import com.ning.billing.util.dao.AuditSqlDao;
 import com.ning.billing.util.dao.BinderBase;
-import com.ning.billing.util.dao.HistorySqlDao;
+import com.ning.billing.util.dao.EntityHistory;
 import com.ning.billing.util.dao.MapperBase;
-import com.ning.billing.util.entity.dao.EntityDao;
-import com.ning.billing.util.entity.dao.EntitySqlDao;
+import com.ning.billing.util.entity.dao.UpdatableEntitySqlDao;
 import org.joda.time.DateTime;
 import org.skife.jdbi.v2.SQLStatement;
 import org.skife.jdbi.v2.StatementContext;
@@ -40,25 +38,22 @@ import org.skife.jdbi.v2.sqlobject.SqlUpdate;
 import org.skife.jdbi.v2.sqlobject.customizers.RegisterMapper;
 import org.skife.jdbi.v2.sqlobject.mixins.CloseMe;
 import org.skife.jdbi.v2.sqlobject.mixins.Transactional;
-import org.skife.jdbi.v2.sqlobject.mixins.Transmogrifier;
 import org.skife.jdbi.v2.sqlobject.stringtemplate.ExternalizedSqlViaStringTemplate3;
 import org.skife.jdbi.v2.tweak.ResultSetMapper;
 import org.skife.jdbi.v2.unstable.BindIn;
 
 import com.ning.billing.payment.api.DefaultPaymentInfoEvent;
-import com.ning.billing.payment.api.PaymentAttempt;
 import com.ning.billing.payment.api.PaymentInfoEvent;
 
 @ExternalizedSqlViaStringTemplate3()
 @RegisterMapper(PaymentSqlDao.PaymentInfoMapper.class)
-public interface PaymentSqlDao extends Transactional<PaymentSqlDao>, EntitySqlDao,
-                                       HistorySqlDao<PaymentInfoEvent>, AuditSqlDao, CloseMe {
+public interface PaymentSqlDao extends Transactional<PaymentSqlDao>, UpdatableEntitySqlDao<PaymentInfoEvent>, CloseMe {
     @SqlQuery
     PaymentInfoEvent getPaymentInfoForPaymentAttemptId(@Bind("payment_attempt_id") String paymentAttemptId);
 
     @SqlUpdate
     void updatePaymentInfo(@Bind("payment_method") String paymentMethod,
-                           @Bind("payment_id") String paymentId,
+                           @Bind("id") String paymentId,
                            @Bind("card_type") String cardType,
                            @Bind("card_country") String cardCountry,
                            @CallContextBinder CallContext context);
@@ -74,12 +69,17 @@ public interface PaymentSqlDao extends Transactional<PaymentSqlDao>, EntitySqlDa
                            @CallContextBinder final CallContext context);
 
     @SqlQuery
-    PaymentInfoEvent getPaymentInfo(@Bind("paymentId") final String paymentId);
+    PaymentInfoEvent getPaymentInfo(@Bind("id") final String paymentId);
+
+    @Override
+    @SqlUpdate
+    public void insertHistoryFromTransaction(@PaymentHistoryBinder final EntityHistory<PaymentInfoEvent> account,
+                                            @CallContextBinder final CallContext context);
 
     public static final class PaymentInfoBinder extends BinderBase implements Binder<Bind, PaymentInfoEvent> {
         @Override
         public void bind(@SuppressWarnings("rawtypes") SQLStatement stmt, Bind bind, PaymentInfoEvent paymentInfo) {
-            stmt.bind("payment_id", paymentInfo.getPaymentId());
+            stmt.bind("id", paymentInfo.getId().toString());
             stmt.bind("amount", paymentInfo.getAmount());
             stmt.bind("refund_amount", paymentInfo.getRefundAmount());
             stmt.bind("payment_number", paymentInfo.getPaymentNumber());
@@ -98,8 +98,7 @@ public interface PaymentSqlDao extends Transactional<PaymentSqlDao>, EntitySqlDa
     public static class PaymentInfoMapper extends MapperBase implements ResultSetMapper<PaymentInfoEvent> {
         @Override
         public PaymentInfoEvent map(int index, ResultSet rs, StatementContext ctx) throws SQLException {
-
-            String paymentId = rs.getString("id");
+            UUID id = getUUID(rs, "id");
             BigDecimal amount = rs.getBigDecimal("amount");
             BigDecimal refundAmount = rs.getBigDecimal("refund_amount");
             String paymentNumber = rs.getString("payment_number");
@@ -115,7 +114,7 @@ public interface PaymentSqlDao extends Transactional<PaymentSqlDao>, EntitySqlDa
 
             UUID userToken = null; //rs.getString("user_token") != null ? UUID.fromString(rs.getString("user_token")) : null;
             
-            return new DefaultPaymentInfoEvent(paymentId,
+            return new DefaultPaymentInfoEvent(id,
                                    amount,
                                    refundAmount,
                                    bankIdentificationNumber,
diff --git a/payment/src/main/java/com/ning/billing/payment/provider/NoOpPaymentProviderPlugin.java b/payment/src/main/java/com/ning/billing/payment/provider/NoOpPaymentProviderPlugin.java
index 625504b..94a97db 100644
--- a/payment/src/main/java/com/ning/billing/payment/provider/NoOpPaymentProviderPlugin.java
+++ b/payment/src/main/java/com/ning/billing/payment/provider/NoOpPaymentProviderPlugin.java
@@ -38,7 +38,7 @@ public class NoOpPaymentProviderPlugin implements PaymentProviderPlugin {
     @Override
     public Either<PaymentErrorEvent, PaymentInfoEvent> processInvoice(Account account, Invoice invoice) {
         PaymentInfoEvent payment = new DefaultPaymentInfoEvent.Builder()
-                                             .setPaymentId(UUID.randomUUID().toString())
+                                             .setId(UUID.randomUUID())
                                              .setAmount(invoice.getBalance())
                                              .setStatus("Processed")
                                              .setEffectiveDate(new DateTime(DateTimeZone.UTC))
diff --git a/payment/src/main/java/com/ning/billing/payment/RequestProcessor.java b/payment/src/main/java/com/ning/billing/payment/RequestProcessor.java
index a1ccd55..2077c5e 100644
--- a/payment/src/main/java/com/ning/billing/payment/RequestProcessor.java
+++ b/payment/src/main/java/com/ning/billing/payment/RequestProcessor.java
@@ -18,23 +18,29 @@ package com.ning.billing.payment;
 
 import java.util.Arrays;
 import java.util.List;
+import java.util.UUID;
 
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import com.google.common.eventbus.EventBus;
 import com.google.common.eventbus.Subscribe;
 import com.google.inject.Inject;
 import com.ning.billing.account.api.Account;
 import com.ning.billing.account.api.AccountApiException;
 import com.ning.billing.account.api.AccountUserApi;
 import com.ning.billing.invoice.api.InvoiceCreationEvent;
+import com.ning.billing.payment.api.DefaultPaymentErrorEvent;
 import com.ning.billing.payment.api.Either;
 import com.ning.billing.payment.api.PaymentApi;
+import com.ning.billing.payment.api.PaymentApiException;
 import com.ning.billing.payment.api.PaymentErrorEvent;
 import com.ning.billing.payment.api.PaymentInfoEvent;
 import com.ning.billing.payment.provider.PaymentProviderPluginRegistry;
 import com.ning.billing.util.bus.Bus;
 import com.ning.billing.util.bus.Bus.EventBusException;
+import com.ning.billing.util.bus.BusEvent.BusEventType;
+import com.ning.billing.util.bus.BusEvent;
 import com.ning.billing.util.callcontext.CallContext;
 import com.ning.billing.util.callcontext.CallOrigin;
 import com.ning.billing.util.callcontext.DefaultCallContext;
@@ -52,39 +58,51 @@ public class RequestProcessor {
 
     @Inject
     public RequestProcessor(Clock clock,
-                            AccountUserApi accountUserApi,
-                            PaymentApi paymentApi,
-                            PaymentProviderPluginRegistry pluginRegistry,
-                            Bus eventBus) {
+            AccountUserApi accountUserApi,
+            PaymentApi paymentApi,
+            PaymentProviderPluginRegistry pluginRegistry,
+            Bus eventBus) {
         this.clock = clock;
         this.accountUserApi = accountUserApi;
         this.paymentApi = paymentApi;
         this.eventBus = eventBus;
     }
+    
+    private void postPaymentEvent(BusEvent ev, UUID accountId) {
+        if (ev == null) {
+            return;
+        }
+        try {
+            eventBus.post(ev);
+        } catch (EventBusException e) {
+            log.error("Failed to post Payment event event for account {} ", accountId, e);
+        }
+    }
 
     @Subscribe
     public void receiveInvoice(InvoiceCreationEvent event) {
+
+
         log.info("Received invoice creation notification for account {} and invoice {}", event.getAccountId(), event.getInvoiceId());
+        PaymentErrorEvent errorEvent = null;
         try {
             final Account account = accountUserApi.getAccountById(event.getAccountId());
-
-            if (account == null) {
-                log.info("could not process invoice payment: could not find a valid account for event {}", event);
-            }
-            else {
+            if (account != null) {
                 CallContext context = new DefaultCallContext("PaymentRequestProcessor", CallOrigin.INTERNAL, UserType.SYSTEM, clock);
-                List<Either<PaymentErrorEvent, PaymentInfoEvent>> results = paymentApi.createPayment(account, Arrays.asList(event.getInvoiceId().toString()), context);
-                if (!results.isEmpty()) {
-                    Either<PaymentErrorEvent, PaymentInfoEvent> result = results.get(0);
-                    eventBus.post(result.isLeft() ? result.getLeft() : result.getRight());
-                }
+                List<PaymentInfoEvent> results = paymentApi.createPayment(account, Arrays.asList(event.getInvoiceId().toString()), context);
+                PaymentInfoEvent infoEvent = (!results.isEmpty()) ?  results.get(0) : null;
+                postPaymentEvent(infoEvent, account.getId());
+                return;
+            } else {
+                errorEvent = new DefaultPaymentErrorEvent(null, "Failed to retrieve account", event.getAccountId(), null, null);
             }
+        } catch(AccountApiException e) {
+            log.error("Failed to process invoice payment", e);
+            errorEvent = new DefaultPaymentErrorEvent(null, e.getMessage(), event.getAccountId(), null, null);            
+        } catch (PaymentApiException e) {
+            log.error("Failed to process invoice payment", e);
+            errorEvent = new DefaultPaymentErrorEvent(null, e.getMessage(), event.getAccountId(), null, null);                        
         }
-        catch(AccountApiException e) {
-            log.warn("could not process invoice payment", e);
-        }
-        catch (EventBusException ex) {
-            throw new RuntimeException(ex);
-        }
+        postPaymentEvent(errorEvent, event.getAccountId());
     }
 }
diff --git a/payment/src/main/java/com/ning/billing/payment/RetryService.java b/payment/src/main/java/com/ning/billing/payment/RetryService.java
index 84c9739..2563638 100644
--- a/payment/src/main/java/com/ning/billing/payment/RetryService.java
+++ b/payment/src/main/java/com/ning/billing/payment/RetryService.java
@@ -24,6 +24,8 @@ import com.ning.billing.util.callcontext.DefaultCallContext;
 import com.ning.billing.util.callcontext.UserType;
 import com.ning.billing.util.clock.Clock;
 import org.joda.time.DateTime;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 import com.google.inject.Inject;
 import com.ning.billing.config.PaymentConfig;
@@ -31,6 +33,7 @@ import com.ning.billing.lifecycle.KillbillService;
 import com.ning.billing.lifecycle.LifecycleHandlerType;
 import com.ning.billing.lifecycle.LifecycleHandlerType.LifecycleLevel;
 import com.ning.billing.payment.api.PaymentApi;
+import com.ning.billing.payment.api.PaymentApiException;
 import com.ning.billing.payment.api.PaymentAttempt;
 import com.ning.billing.payment.api.PaymentInfoEvent;
 import com.ning.billing.payment.api.PaymentStatus;
@@ -42,6 +45,9 @@ import com.ning.billing.util.notificationq.NotificationQueueService.Notification
 import com.ning.billing.util.notificationq.NotificationQueueService.NotificationQueueHandler;
 
 public class RetryService implements KillbillService {
+    
+    private static final Logger log = LoggerFactory.getLogger(RetryService.class);
+    
     public static final String SERVICE_NAME = "retry-service";
     public static final String QUEUE_NAME = "retry-events";
 
@@ -107,15 +113,14 @@ public class RetryService implements KillbillService {
     }
 
     private void retry(String paymentAttemptId, CallContext context) {
-        PaymentInfoEvent paymentInfo = paymentApi.getPaymentInfoForPaymentAttemptId(paymentAttemptId);
-
-        if (paymentInfo != null && PaymentStatus.Processed.equals(PaymentStatus.valueOf(paymentInfo.getStatus()))) {
-            // update payment attempt with success and notify invoice api of payment
-            System.out.println("Found processed payment");
-        }
-        else {
-            System.out.println("Creating payment for payment attempt " + paymentAttemptId);
+        try {
+            PaymentInfoEvent paymentInfo = paymentApi.getPaymentInfoForPaymentAttemptId(paymentAttemptId);
+            if (paymentInfo != null && PaymentStatus.Processed.equals(PaymentStatus.valueOf(paymentInfo.getStatus()))) {
+                return;
+            }
             paymentApi.createPaymentForPaymentAttempt(UUID.fromString(paymentAttemptId), context);
+        } catch (PaymentApiException e) {
+            log.error(String.format("Failed to retry payment for %s"), e);
         }
     }
 }
diff --git a/payment/src/main/resources/com/ning/billing/payment/dao/PaymentAttemptSqlDao.sql.stg b/payment/src/main/resources/com/ning/billing/payment/dao/PaymentAttemptSqlDao.sql.stg
new file mode 100644
index 0000000..812da6a
--- /dev/null
+++ b/payment/src/main/resources/com/ning/billing/payment/dao/PaymentAttemptSqlDao.sql.stg
@@ -0,0 +1,106 @@
+group paymentAttemptSqlDao;
+
+paymentAttemptFields(prefix) ::= <<
+    <prefix>id,
+    <prefix>invoice_id,
+    <prefix>account_id,
+    <prefix>amount,
+    <prefix>currency,
+    <prefix>payment_id,
+    <prefix>payment_attempt_date,
+    <prefix>invoice_date,
+    <prefix>retry_count,
+    <prefix>created_by,
+    <prefix>created_date,
+    <prefix>updated_by,
+    <prefix>updated_date
+>>
+
+insertPaymentAttempt() ::= <<
+    INSERT INTO payment_attempts (<paymentAttemptFields()>)
+    VALUES (:id, :invoice_id, :account_id, :amount, :currency, :payment_id,
+            :payment_attempt_date, :invoice_date, :retry_count, :userName, :createdDate, :userName, :createdDate);
+>>
+
+getPaymentAttemptForPaymentId() ::= <<
+    SELECT <paymentAttemptFields()>
+      FROM payment_attempts
+     WHERE id = :id;
+>>
+
+getPaymentAttemptById() ::= <<
+    SELECT <paymentAttemptFields()>
+      FROM payment_attempts
+     WHERE id = :id;
+>>
+
+getPaymentAttemptsForInvoiceIds(invoiceIds) ::= <<
+    SELECT <paymentAttemptFields()>
+      FROM payment_attempts
+     WHERE invoice_id in (<invoiceIds>);
+>>
+
+getPaymentAttemptsForInvoiceId() ::= <<
+    SELECT <paymentAttemptFields()>
+      FROM payment_attempts
+     WHERE invoice_id = :invoice_id;
+>>
+
+updatePaymentAttemptWithPaymentId() ::= <<
+    UPDATE payment_attempts
+       SET payment_id = :payment_id,
+           updated_by = :userName,
+           updated_date = :updatedDate
+     WHERE id = :id;
+>>
+
+historyFields(prefix) ::= <<
+    record_id,
+    id,
+    account_id,
+    invoice_id,
+    amount,
+    currency,
+    payment_attempt_date,
+    payment_id,
+    retry_count,
+    invoice_date,
+    created_by,
+    created_date,
+    updated_by,
+    updated_date
+>>
+
+insertHistoryFromTransaction() ::= <<
+    INSERT INTO payment_attempt_history (<historyFields()>)
+    VALUES (:recordId, :id, :account_id, :invoice_id, :amount, :currency, :payment_attempt_date, :payment_id,
+            :retry_count, :invoice_date, :userName, :createdDate, :userName, :updatedDate);
+>>
+
+getRecordId() ::= <<
+    SELECT record_id
+    FROM payment_attempts
+    WHERE id = :id;
+>>
+
+getHistoryRecordId() ::= <<
+    SELECT MAX(history_record_id)
+    FROM payment_attempt_history
+    WHERE record_id = :recordId;
+>>
+
+auditFields(prefix) ::= <<
+    <prefix>table_name,
+    <prefix>record_id,
+    <prefix>change_type,
+    <prefix>change_date,
+    <prefix>changed_by,
+    <prefix>reason_code,
+    <prefix>comments,
+    <prefix>user_token
+>>
+
+insertAuditFromTransaction() ::= <<
+    INSERT INTO audit_log(<auditFields()>)
+    VALUES(:tableName, :recordId, :changeType, :createdDate, :userName, NULL, NULL, NULL);
+>>
diff --git a/payment/src/main/resources/com/ning/billing/payment/dao/PaymentSqlDao.sql.stg b/payment/src/main/resources/com/ning/billing/payment/dao/PaymentSqlDao.sql.stg
index 473caa6..3f125ab 100644
--- a/payment/src/main/resources/com/ning/billing/payment/dao/PaymentSqlDao.sql.stg
+++ b/payment/src/main/resources/com/ning/billing/payment/dao/PaymentSqlDao.sql.stg
@@ -1,23 +1,7 @@
 group PaymentSqlDao;
 
-paymentAttemptFields(prefix) ::= <<
-    <prefix>payment_attempt_id,
-    <prefix>invoice_id,
-    <prefix>account_id,
-    <prefix>amount,
-    <prefix>currency,
-    <prefix>payment_id,
-    <prefix>payment_attempt_date,
-    <prefix>invoice_date,
-    <prefix>retry_count,
-    <prefix>created_by,
-    <prefix>created_date,
-    <prefix>updated_by,
-    <prefix>updated_date
->>
-
 paymentInfoFields(prefix) ::= <<
-    <prefix>payment_id,
+    <prefix>id,
     <prefix>amount,
     <prefix>refund_amount,
     <prefix>bank_identification_number,
@@ -36,60 +20,9 @@ paymentInfoFields(prefix) ::= <<
     <prefix>updated_date
 >>
 
-insertPaymentAttempt() ::= <<
-    INSERT INTO payment_attempts (<paymentAttemptFields()>)
-    VALUES (:payment_attempt_id, :invoice_id, :account_id, :amount, :currency, :payment_id,
-            :payment_attempt_date, :invoice_date, :retry_count, :userName, :createdDate, :userName, :createdDate);
->>
-
-insertPaymentAttemptHistory() ::= <<
-    INSERT INTO payment_attempt_history (history_record_id, <paymentAttemptFields()>)
-    VALUES (:historyRecordId, :payment_attempt_id, :invoice_id, :account_id, :amount, :currency, :payment_id,
-            :payment_attempt_date, :invoice_date, :retry_count, :userName, :createdDate, :userName, :createdDate);
->>
-
-getPaymentAttemptForPaymentId() ::= <<
-    SELECT <paymentAttemptFields()>
-      FROM payment_attempts
-     WHERE payment_id = :payment_id
->>
-
-getPaymentAttemptById() ::= <<
-    SELECT <paymentAttemptFields()>
-      FROM payment_attempts
-     WHERE payment_attempt_id = :payment_attempt_id
->>
-
-getPaymentAttemptsForInvoiceIds(invoiceIds) ::= <<
-    SELECT <paymentAttemptFields()>
-      FROM payment_attempts
-     WHERE invoice_id in (<invoiceIds>)
->>
-
-getPaymentAttemptsForInvoiceId() ::= <<
-    SELECT <paymentAttemptFields()>
-      FROM payment_attempts
-     WHERE invoice_id = :invoice_id
->>
-
-updatePaymentAttemptWithPaymentId() ::= <<
-    UPDATE payment_attempts
-       SET payment_id = :payment_id,
-           updated_by = :userName,
-           updated_date = :updatedDate
-     WHERE payment_attempt_id = :payment_attempt_id
->>
-
 insertPaymentInfo() ::= <<
     INSERT INTO payments (<paymentInfoFields()>)
-    VALUES (:payment_id, :amount, :refund_amount, :bank_identification_number, :payment_number,
-    :payment_type, :status, :reference_id, :payment_method_id, :payment_method, :card_type,
-    :card_country, :effective_date, :userName, :createdDate, :userName, :createdDate);
->>
-
-insertPaymentInfoHistory() ::= <<
-    INSERT INTO payment_history (history_record_id, <paymentInfoFields()>)
-    VALUES (:historyRecordId, :payment_id, :amount, :refund_amount, :bank_identification_number, :payment_number,
+    VALUES (:id, :amount, :refund_amount, :bank_identification_number, :payment_number,
     :payment_type, :status, :reference_id, :payment_method_id, :payment_method, :card_type,
     :card_country, :effective_date, :userName, :createdDate, :userName, :createdDate);
 >>
@@ -101,25 +34,91 @@ updatePaymentInfo() ::= <<
            card_country = :card_country,
            updated_by = :userName,
            updated_date = :updatedDate
-     WHERE payment_id = :payment_id
+     WHERE id = :id
 >>
 
-getPaymentInfos(invoiceIds) ::= <<
+getPaymentInfoList(invoiceIds) ::= <<
     SELECT <paymentInfoFields("p.")>
       FROM payments p, payment_attempts pa
-     WHERE pa.invoice_id in (<invoiceIds>)
-       AND pa.payment_id = p.payment_id
+    WHERE pa.invoice_id in (<invoiceIds>)
+       AND pa.payment_id = p.id
+>>
+
+getLastPaymentInfo(invoiceIds) ::= <<
+    SELECT <paymentInfoFields("p.")>
+    FROM payments p, payment_attempts pa
+    WHERE pa.invoice_id in (<invoiceIds>)
+    AND pa.payment_id = p.id
+    ORDER BY p.created_date DESC
+    LIMIT 1;
 >>
 
 getPaymentInfoForPaymentAttemptId() ::= <<
     SELECT <paymentInfoFields("p.")>
       FROM payments p, payment_attempts pa
-     WHERE pa.payment_attempt_id = :payment_attempt_id
-       AND pa.payment_id = p.payment_id
+    WHERE pa.payment_attempt_id = :payment_attempt_id
+       AND pa.payment_id = p.id
 >>
 
 getPaymentInfo() ::= <<
     SELECT <paymentInfoFields()>
     FROM payments
-    WHERE payment_id = :paymentId
+    WHERE id = :id
+>>
+
+historyFields(prefix) ::= <<
+    record_id,
+    id,
+    amount,
+    refund_amount,
+    payment_number,
+    bank_identification_number,
+    status,
+    reference_id,
+    payment_type,
+    payment_method_id,
+    payment_method,
+    card_type,
+    card_country,
+    effective_date,
+    created_by,
+    created_date,
+    updated_by,
+    updated_date
+>>
+
+insertHistoryFromTransaction() ::= <<
+    INSERT INTO payment_history (<historyFields()>)
+    VALUES (:recordId, :id, :amount, :refund_amount, :bank_identification_number, :payment_number,
+    :payment_type, :status, :reference_id, :payment_method_id, :payment_method, :card_type,
+    :card_country, :effective_date, :userName, :createdDate, :userName, :updatedDate);
+>>
+
+getRecordId() ::= <<
+    SELECT record_id
+    FROM payments
+    WHERE id = :id;
+>>
+
+getHistoryRecordId() ::= <<
+    SELECT MAX(history_record_id)
+    FROM payment_history
+    WHERE record_id = :recordId;
 >>
+
+auditFields(prefix) ::= <<
+    <prefix>table_name,
+    <prefix>record_id,
+    <prefix>change_type,
+    <prefix>change_date,
+    <prefix>changed_by,
+    <prefix>reason_code,
+    <prefix>comments,
+    <prefix>user_token
+>>
+
+insertAuditFromTransaction() ::= <<
+    INSERT INTO audit_log(<auditFields()>)
+    VALUES(:tableName, :recordId, :changeType, :createdDate, :userName, NULL, NULL, NULL);
+>>
+
diff --git a/payment/src/test/java/com/ning/billing/payment/api/TestEventJson.java b/payment/src/test/java/com/ning/billing/payment/api/TestEventJson.java
index b973d36..198e986 100644
--- a/payment/src/test/java/com/ning/billing/payment/api/TestEventJson.java
+++ b/payment/src/test/java/com/ning/billing/payment/api/TestEventJson.java
@@ -49,13 +49,13 @@ public class TestEventJson {
     
     @Test(groups= {"fast"})
     public void testPaymentInfoEvent() throws Exception {
-        PaymentInfoEvent e = new DefaultPaymentInfoEvent(UUID.randomUUID().toString(), new BigDecimal(12), new BigDecimal(12.9), "BNP", "eeert", "success",
+        PaymentInfoEvent e = new DefaultPaymentInfoEvent(UUID.randomUUID(), new BigDecimal(12), new BigDecimal(12.9), "BNP", "eeert", "success",
                 "credit", "ref", "paypal", "paypal", "", "", UUID.randomUUID(), new DateTime());
         
         String json = mapper.writeValueAsString(e);
 
-        Class<?> claz = Class.forName(DefaultPaymentInfoEvent.class.getName());
-        Object obj =  mapper.readValue(json, claz);
+        Class<?> clazz = Class.forName(DefaultPaymentInfoEvent.class.getName());
+        Object obj =  mapper.readValue(json, clazz);
         Assert.assertTrue(obj.equals(e));
     }
 }
diff --git a/payment/src/test/java/com/ning/billing/payment/api/TestMockPaymentApi.java b/payment/src/test/java/com/ning/billing/payment/api/TestMockPaymentApi.java
index 833f926..0ff39d2 100644
--- a/payment/src/test/java/com/ning/billing/payment/api/TestMockPaymentApi.java
+++ b/payment/src/test/java/com/ning/billing/payment/api/TestMockPaymentApi.java
@@ -20,13 +20,13 @@ import org.testng.annotations.Guice;
 import org.testng.annotations.Test;
 
 import com.google.inject.Inject;
-import com.ning.billing.account.glue.AccountModuleWithMocks;
-import com.ning.billing.invoice.glue.InvoiceModuleWithMocks;
 import com.ning.billing.mock.glue.MockJunctionModule;
 import com.ning.billing.payment.setup.PaymentTestModuleWithMocks;
 import com.ning.billing.util.clock.Clock;
+import com.ning.billing.util.clock.MockClockModule;
+import com.ning.billing.util.glue.CallContextModule;
 
-@Guice(modules = { PaymentTestModuleWithMocks.class, AccountModuleWithMocks.class, InvoiceModuleWithMocks.class, MockJunctionModule.class })
+@Guice(modules = { PaymentTestModuleWithMocks.class, MockClockModule.class, MockJunctionModule.class, CallContextModule.class })
 @Test(groups = "fast")
 public class TestMockPaymentApi extends TestPaymentApi {
     @Inject
diff --git a/payment/src/test/java/com/ning/billing/payment/api/TestPaymentApi.java b/payment/src/test/java/com/ning/billing/payment/api/TestPaymentApi.java
index 53635fb..c7f925b 100644
--- a/payment/src/test/java/com/ning/billing/payment/api/TestPaymentApi.java
+++ b/payment/src/test/java/com/ning/billing/payment/api/TestPaymentApi.java
@@ -36,12 +36,12 @@ import org.testng.annotations.Test;
 
 import com.google.inject.Inject;
 import com.ning.billing.account.api.Account;
-import com.ning.billing.account.api.AccountApiException;
 import com.ning.billing.catalog.api.Currency;
 import com.ning.billing.invoice.api.Invoice;
-import com.ning.billing.invoice.model.RecurringInvoiceItem;
+import com.ning.billing.invoice.api.InvoicePaymentApi;
 import com.ning.billing.mock.BrainDeadProxyFactory;
 import com.ning.billing.mock.BrainDeadProxyFactory.ZombieControl;
+import com.ning.billing.payment.MockRecurringInvoiceItem;
 import com.ning.billing.payment.TestHelper;
 import com.ning.billing.util.bus.Bus;
 import com.ning.billing.util.bus.Bus.EventBusException;
@@ -50,7 +50,6 @@ import com.ning.billing.util.callcontext.CallOrigin;
 import com.ning.billing.util.callcontext.DefaultCallContext;
 import com.ning.billing.util.callcontext.UserType;
 import com.ning.billing.util.clock.Clock;
-import com.ning.billing.util.entity.EntityPersistenceException;
 
 public abstract class TestPaymentApi {
     @Inject
@@ -59,6 +58,8 @@ public abstract class TestPaymentApi {
     protected PaymentApi paymentApi;
     @Inject
     protected TestHelper testHelper;
+    @Inject
+    protected InvoicePaymentApi invoicePaymentApi;
 
     protected CallContext context;
 
@@ -78,7 +79,9 @@ public abstract class TestPaymentApi {
     }
 
     @Test(enabled=true)
-    public void testCreateCreditCardPayment() throws AccountApiException, EntityPersistenceException {
+    public void testCreateCreditCardPayment() throws Exception {
+        ((ZombieControl)invoicePaymentApi).addResult("notifyOfPaymentAttempt", BrainDeadProxyFactory.ZOMBIE_VOID);
+
         final DateTime now = new DateTime(DateTimeZone.UTC);
         final Account account = testHelper.createTestCreditCardAccount();
         final Invoice invoice = testHelper.createTestInvoice(account, now, Currency.USD);
@@ -86,7 +89,7 @@ public abstract class TestPaymentApi {
         final UUID subscriptionId = UUID.randomUUID();
         final UUID bundleId = UUID.randomUUID();
 
-        invoice.addInvoiceItem(new RecurringInvoiceItem(invoice.getId(), account.getId(),
+        invoice.addInvoiceItem(new MockRecurringInvoiceItem(invoice.getId(), account.getId(),
                                                        subscriptionId,
                                                        bundleId,
                                                        "test plan", "test phase",
@@ -96,25 +99,24 @@ public abstract class TestPaymentApi {
                                                        new BigDecimal("1.0"),
                                                        Currency.USD));
 
-        List<Either<PaymentErrorEvent, PaymentInfoEvent>> results = paymentApi.createPayment(account.getExternalKey(), Arrays.asList(invoice.getId().toString()), context);
+        List<PaymentInfoEvent> results = paymentApi.createPayment(account.getExternalKey(), Arrays.asList(invoice.getId().toString()), context);
 
         assertEquals(results.size(), 1);
-        assertTrue(results.get(0).isRight());
 
-        PaymentInfoEvent paymentInfo = results.get(0).getRight();
+        PaymentInfoEvent paymentInfo = results.get(0);
 
-        assertNotNull(paymentInfo.getPaymentId());
+        assertNotNull(paymentInfo.getId());
         assertTrue(paymentInfo.getAmount().compareTo(amount.setScale(2, RoundingMode.HALF_EVEN)) == 0);
         assertNotNull(paymentInfo.getPaymentNumber());
         assertFalse(paymentInfo.getStatus().equals("Error"));
 
-        PaymentAttempt paymentAttempt = paymentApi.getPaymentAttemptForPaymentId(paymentInfo.getPaymentId());
+        PaymentAttempt paymentAttempt = paymentApi.getPaymentAttemptForPaymentId(paymentInfo.getId());
         assertNotNull(paymentAttempt);
         assertNotNull(paymentAttempt.getId());
         assertEquals(paymentAttempt.getInvoiceId(), invoice.getId());
         assertTrue(paymentAttempt.getAmount().compareTo(amount.setScale(2, RoundingMode.HALF_EVEN)) == 0);
         assertEquals(paymentAttempt.getCurrency(), Currency.USD);
-        assertEquals(paymentAttempt.getPaymentId(), paymentInfo.getPaymentId());
+        assertEquals(paymentAttempt.getPaymentId(), paymentInfo.getId());
         DateTime nowTruncated = now.withMillisOfSecond(0).withSecondOfMinute(0);
         DateTime paymentAttemptDateTruncated = paymentAttempt.getPaymentAttemptDate().withMillisOfSecond(0).withSecondOfMinute(0);
         assertEquals(paymentAttemptDateTruncated.compareTo(nowTruncated), 0);
@@ -126,7 +128,7 @@ public abstract class TestPaymentApi {
         PaymentInfoEvent paymentInfoFromGet = paymentInfos.get(0);
         assertEquals(paymentInfo.getAmount(), paymentInfoFromGet.getAmount());
         assertEquals(paymentInfo.getRefundAmount(), paymentInfoFromGet.getRefundAmount());
-        assertEquals(paymentInfo.getPaymentId(), paymentInfoFromGet.getPaymentId());
+        assertEquals(paymentInfo.getId(), paymentInfoFromGet.getId());
         assertEquals(paymentInfo.getPaymentNumber(), paymentInfoFromGet.getPaymentNumber());
         assertEquals(paymentInfo.getStatus(), paymentInfoFromGet.getStatus());
         assertEquals(paymentInfo.getBankIdentificationNumber(), paymentInfoFromGet.getBankIdentificationNumber());
@@ -139,7 +141,7 @@ public abstract class TestPaymentApi {
 
     }
 
-    private PaymentProviderAccount setupAccountWithPaypalPaymentMethod() throws AccountApiException, EntityPersistenceException {
+    private PaymentProviderAccount setupAccountWithPaypalPaymentMethod() throws Exception  {
         final Account account = testHelper.createTestPayPalAccount();
         paymentApi.createPaymentProviderAccount(account, context);
 
@@ -150,32 +152,22 @@ public abstract class TestPaymentApi {
                                                                            .setEmail(account.getEmail())
                                                                            .setDefaultMethod(true)
                                                                            .build();
-        Either<PaymentErrorEvent, String> paymentMethodIdOrError = paymentApi.addPaymentMethod(accountKey, paymentMethod, context);
-
-        assertTrue(paymentMethodIdOrError.isRight());
-        assertNotNull(paymentMethodIdOrError.getRight());
-
-        Either<PaymentErrorEvent, PaymentMethodInfo> paymentMethodInfoOrError = paymentApi.getPaymentMethod(accountKey, paymentMethodIdOrError.getRight());
+        String paymentMethodId = paymentApi.addPaymentMethod(accountKey, paymentMethod, context);
 
-        assertTrue(paymentMethodInfoOrError.isRight());
-        assertNotNull(paymentMethodInfoOrError.getRight());
+        PaymentMethodInfo paymentMethodInfo = paymentApi.getPaymentMethod(accountKey, paymentMethodId);
 
-        Either<PaymentErrorEvent, PaymentProviderAccount> accountOrError = paymentApi.getPaymentProviderAccount(accountKey);
-
-        assertTrue(accountOrError.isRight());
-
-        return accountOrError.getRight();
+        return paymentApi.getPaymentProviderAccount(accountKey);
     }
 
     @Test(enabled=true)
-    public void testCreatePaypalPaymentMethod() throws AccountApiException, EntityPersistenceException {
+    public void testCreatePaypalPaymentMethod() throws Exception  {
         PaymentProviderAccount account = setupAccountWithPaypalPaymentMethod();
         assertNotNull(account);
         paymentApi.getPaymentMethods(account.getAccountKey());
     }
 
     @Test(enabled=true)
-    public void testUpdatePaymentProviderAccountContact() throws AccountApiException, EntityPersistenceException {
+    public void testUpdatePaymentProviderAccountContact() throws Exception {
         final Account account = testHelper.createTestPayPalAccount();
         paymentApi.createPaymentProviderAccount(account, context);
 
@@ -190,48 +182,12 @@ public abstract class TestPaymentApi {
         zombieAccount.addResult("getCurrency", account.getCurrency());
         zombieAccount.addResult("getBillCycleDay", account.getBillCycleDay());
 
-        Either<PaymentErrorEvent, Void> voidOrError = paymentApi.updatePaymentProviderAccountContact(account.getExternalKey(), context);
-        assertTrue(voidOrError.isRight());
+        paymentApi.updatePaymentProviderAccountContact(updatedAccount.getExternalKey(), context);
     }
 
     @Test(enabled=true)
-    public void testCannotDeleteDefaultPaymentMethod() throws AccountApiException, EntityPersistenceException {
+    public void testCannotDeleteDefaultPaymentMethod() throws Exception  {
         PaymentProviderAccount account = setupAccountWithPaypalPaymentMethod();
-
-        Either<PaymentErrorEvent, Void> errorOrVoid = paymentApi.deletePaymentMethod(account.getAccountKey(), account.getDefaultPaymentMethodId(), context);
-
-        assertTrue(errorOrVoid.isLeft());
+        paymentApi.deletePaymentMethod(account.getAccountKey(), account.getDefaultPaymentMethodId(), context);
     }
-
-    @Test(enabled=true)
-    public void testDeleteNonDefaultPaymentMethod() throws AccountApiException, EntityPersistenceException {
-        final Account account = testHelper.createTestPayPalAccount();
-        paymentApi.createPaymentProviderAccount(account, context);
-
-        String accountKey = account.getExternalKey();
-
-        PaypalPaymentMethodInfo paymentMethod1 = new PaypalPaymentMethodInfo.Builder().setDefaultMethod(false).setBaid("12345").setEmail(account.getEmail()).build();
-        Either<PaymentErrorEvent, String> paymentMethodIdOrError1 = paymentApi.addPaymentMethod(accountKey, paymentMethod1, context);
-
-        assertTrue(paymentMethodIdOrError1.isRight());
-        assertNotNull(paymentMethodIdOrError1.getRight());
-
-        PaypalPaymentMethodInfo paymentMethod2 = new PaypalPaymentMethodInfo.Builder().setDefaultMethod(true).setBaid("12345").setEmail(account.getEmail()).build();
-
-        Either<PaymentErrorEvent, String> paymentMethodIdOrError2 = paymentApi.addPaymentMethod(accountKey, paymentMethod2, context);
-
-        assertTrue(paymentMethodIdOrError2.isRight());
-        assertNotNull(paymentMethodIdOrError2.getRight());
-
-        Either<PaymentErrorEvent, List<PaymentMethodInfo>> paymentMethodsOrError = paymentApi.getPaymentMethods(accountKey);
-
-        assertTrue(paymentMethodsOrError.isRight());
-
-        Either<PaymentErrorEvent, Void> errorOrVoid1 = paymentApi.deletePaymentMethod(accountKey, paymentMethodIdOrError1.getRight(), context);
-        Either<PaymentErrorEvent, Void> errorOrVoid2 = paymentApi.deletePaymentMethod(accountKey, paymentMethodIdOrError2.getRight(), context);
-
-        assertTrue(errorOrVoid1.isRight());
-        assertTrue(errorOrVoid2.isLeft());
-    }
-
 }
diff --git a/payment/src/test/java/com/ning/billing/payment/dao/MockPaymentDao.java b/payment/src/test/java/com/ning/billing/payment/dao/MockPaymentDao.java
index 85ff33e..3f90466 100644
--- a/payment/src/test/java/com/ning/billing/payment/dao/MockPaymentDao.java
+++ b/payment/src/test/java/com/ning/billing/payment/dao/MockPaymentDao.java
@@ -35,11 +35,11 @@ import com.ning.billing.payment.api.PaymentAttempt;
 import com.ning.billing.payment.api.PaymentInfoEvent;
 
 public class MockPaymentDao implements PaymentDao {
-    private final Map<String, PaymentInfoEvent> payments = new ConcurrentHashMap<String, PaymentInfoEvent>();
+    private final Map<UUID, PaymentInfoEvent> payments = new ConcurrentHashMap<UUID, PaymentInfoEvent>();
     private final Map<UUID, PaymentAttempt> paymentAttempts = new ConcurrentHashMap<UUID, PaymentAttempt>();
 
     @Override
-    public PaymentAttempt getPaymentAttemptForPaymentId(String paymentId) {
+    public PaymentAttempt getPaymentAttemptForPaymentId(UUID paymentId) {
         for (PaymentAttempt paymentAttempt : paymentAttempts.values()) {
             if (paymentId.equals(paymentAttempt.getPaymentId())) {
                 return paymentAttempt;
@@ -52,7 +52,7 @@ public class MockPaymentDao implements PaymentDao {
     public PaymentAttempt createPaymentAttempt(Invoice invoice, CallContext context) {
         PaymentAttempt updatedPaymentAttempt = new DefaultPaymentAttempt(UUID.randomUUID(), invoice.getId(), invoice.getAccountId(),
                 invoice.getBalance(), invoice.getCurrency(), invoice.getInvoiceDate(),
-                null, null, null);
+                null, null, null, null, null);
 
         paymentAttempts.put(updatedPaymentAttempt.getId(), updatedPaymentAttempt);
         return updatedPaymentAttempt;
@@ -64,7 +64,8 @@ public class MockPaymentDao implements PaymentDao {
                 paymentAttempt.getInvoiceId(),
                 paymentAttempt.getAccountId(), paymentAttempt.getAmount(), paymentAttempt.getCurrency(),
                 paymentAttempt.getInvoiceDate(), paymentAttempt.getPaymentAttemptDate(),
-                paymentAttempt.getPaymentId(), paymentAttempt.getRetryCount());
+                paymentAttempt.getPaymentId(), paymentAttempt.getRetryCount(),
+                paymentAttempt.getCreatedDate(), paymentAttempt.getUpdatedDate());
 
         paymentAttempts.put(updatedPaymentAttempt.getId(), updatedPaymentAttempt);
         return updatedPaymentAttempt;
@@ -72,11 +73,11 @@ public class MockPaymentDao implements PaymentDao {
 
     @Override
     public void savePaymentInfo(PaymentInfoEvent paymentInfo, CallContext context) {
-        payments.put(paymentInfo.getPaymentId(), paymentInfo);
+        payments.put(paymentInfo.getId(), paymentInfo);
     }
 
     @Override
-    public void updatePaymentAttemptWithPaymentId(UUID paymentAttemptId, String paymentId, CallContext context) {
+    public void updatePaymentAttemptWithPaymentId(UUID paymentAttemptId, UUID paymentId, CallContext context) {
         PaymentAttempt existingPaymentAttempt = paymentAttempts.get(paymentAttemptId);
 
         if (existingPaymentAttempt != null) {
@@ -97,7 +98,7 @@ public class MockPaymentDao implements PaymentDao {
     }
 
     @Override
-    public void updatePaymentInfo(String paymentMethodType, String paymentId, String cardType, String cardCountry, CallContext context) {
+    public void updatePaymentInfo(String paymentMethodType, UUID paymentId, String cardType, String cardCountry, CallContext context) {
         DefaultPaymentInfoEvent existingPayment = (DefaultPaymentInfoEvent) payments.get(paymentId);
         if (existingPayment != null) {
             PaymentInfoEvent payment = existingPayment.cloner()
@@ -118,7 +119,7 @@ public class MockPaymentDao implements PaymentDao {
             paymentsToReturn.addAll(Collections2.filter(payments.values(), new Predicate<PaymentInfoEvent>() {
                 @Override
                 public boolean apply(PaymentInfoEvent input) {
-                    return input.getPaymentId().equals(attempt.getPaymentId());
+                    return input.getId().equals(attempt.getPaymentId());
                 }
             }));
         }
diff --git a/payment/src/test/java/com/ning/billing/payment/dao/TestPaymentDao.java b/payment/src/test/java/com/ning/billing/payment/dao/TestPaymentDao.java
index 232a448..40b92ef 100644
--- a/payment/src/test/java/com/ning/billing/payment/dao/TestPaymentDao.java
+++ b/payment/src/test/java/com/ning/billing/payment/dao/TestPaymentDao.java
@@ -44,7 +44,7 @@ public abstract class TestPaymentDao {
 
     @Test
     public void testCreatePayment() {
-        PaymentInfoEvent paymentInfo = new DefaultPaymentInfoEvent.Builder().setPaymentId(UUID.randomUUID().toString())
+        PaymentInfoEvent paymentInfo = new DefaultPaymentInfoEvent.Builder().setId(UUID.randomUUID())
                 .setAmount(BigDecimal.TEN)
                 .setStatus("Processed")
                 .setBankIdentificationNumber("1234")
@@ -60,7 +60,7 @@ public abstract class TestPaymentDao {
 
     @Test
     public void testUpdatePaymentInfo() {
-        PaymentInfoEvent paymentInfo = new DefaultPaymentInfoEvent.Builder().setPaymentId(UUID.randomUUID().toString())
+        PaymentInfoEvent paymentInfo = new DefaultPaymentInfoEvent.Builder().setId(UUID.randomUUID())
                 .setAmount(BigDecimal.TEN)
                 .setStatus("Processed")
                 .setBankIdentificationNumber("1234")
@@ -73,13 +73,13 @@ public abstract class TestPaymentDao {
 
         CallContext context = new TestCallContext("PaymentTests");
         paymentDao.savePaymentInfo(paymentInfo, context);
-        paymentDao.updatePaymentInfo("CreditCard", paymentInfo.getPaymentId(), "Visa", "US", context);
+        paymentDao.updatePaymentInfo("CreditCard", paymentInfo.getId(), "Visa", "US", context);
     }
 
     @Test
     public void testUpdatePaymentAttempt() {
         PaymentAttempt paymentAttempt = new DefaultPaymentAttempt.Builder().setPaymentAttemptId(UUID.randomUUID())
-                .setPaymentId(UUID.randomUUID().toString())
+                .setPaymentId(UUID.randomUUID())
                 .setInvoiceId(UUID.randomUUID())
                 .setAccountId(UUID.randomUUID())
                 .setAmount(BigDecimal.TEN)
@@ -95,14 +95,14 @@ public abstract class TestPaymentDao {
         final UUID invoiceId = UUID.randomUUID();
         final UUID paymentAttemptId = UUID.randomUUID();
         final UUID accountId = UUID.randomUUID();
-        final String paymentId = UUID.randomUUID().toString();
+        final UUID paymentId = UUID.randomUUID();
         final BigDecimal invoiceAmount = BigDecimal.TEN;
 
         // Move the clock backwards to test the updated_date field (see below)
         ClockMock clock = new ClockMock();
         CallContext thisContext = new DefaultCallContext("Payment Tests", CallOrigin.TEST, UserType.TEST, clock);
 
-        PaymentAttempt originalPaymentAttempt = new DefaultPaymentAttempt(paymentAttemptId, invoiceId, accountId, invoiceAmount, Currency.USD, clock.getUTCNow(), clock.getUTCNow(), paymentId, 0);
+        PaymentAttempt originalPaymentAttempt = new DefaultPaymentAttempt(paymentAttemptId, invoiceId, accountId, invoiceAmount, Currency.USD, clock.getUTCNow(), clock.getUTCNow(), paymentId, 0, null, null);
         PaymentAttempt attempt = paymentDao.createPaymentAttempt(originalPaymentAttempt, thisContext);
 
         List<PaymentAttempt> attemptsFromGet = paymentDao.getPaymentAttemptsForInvoiceId(invoiceId.toString());
@@ -117,7 +117,7 @@ public abstract class TestPaymentDao {
 
         Assert.assertEquals(attempt3, attempt4);
 
-        PaymentInfoEvent originalPaymentInfo = new DefaultPaymentInfoEvent.Builder().setPaymentId(paymentId)
+        PaymentInfoEvent originalPaymentInfo = new DefaultPaymentInfoEvent.Builder().setId(paymentId)
                 .setAmount(invoiceAmount)
                 .setStatus("Processed")
                 .setBankIdentificationNumber("1234")
@@ -133,7 +133,7 @@ public abstract class TestPaymentDao {
         Assert.assertEquals(paymentInfo, originalPaymentInfo);
 
         clock.setDeltaFromReality(60 * 60 * 1000); // move clock forward one hour
-        paymentDao.updatePaymentInfo(originalPaymentInfo.getPaymentMethod(), originalPaymentInfo.getPaymentId(), originalPaymentInfo.getCardType(), originalPaymentInfo.getCardCountry(), thisContext);
+        paymentDao.updatePaymentInfo(originalPaymentInfo.getPaymentMethod(), originalPaymentInfo.getId(), originalPaymentInfo.getCardType(), originalPaymentInfo.getCardCountry(), thisContext);
         paymentInfo = paymentDao.getPaymentInfoList(Arrays.asList(invoiceId.toString())).get(0);
         // TODO: replace these asserts
 //        Assert.assertEquals(paymentInfo.getCreatedDate().compareTo(attempt.getCreatedDate()), 0);
diff --git a/payment/src/test/java/com/ning/billing/payment/dao/TestPaymentDaoWithEmbeddedDb.java b/payment/src/test/java/com/ning/billing/payment/dao/TestPaymentDaoWithEmbeddedDb.java
index 84e3e5a..7c2fe65 100644
--- a/payment/src/test/java/com/ning/billing/payment/dao/TestPaymentDaoWithEmbeddedDb.java
+++ b/payment/src/test/java/com/ning/billing/payment/dao/TestPaymentDaoWithEmbeddedDb.java
@@ -33,9 +33,11 @@ public class TestPaymentDaoWithEmbeddedDb extends TestPaymentDao {
     @BeforeClass(groups = { "slow", "database" })
     public void startMysql() throws IOException {
         final String paymentddl = IOUtils.toString(MysqlTestingHelper.class.getResourceAsStream("/com/ning/billing/payment/ddl.sql"));
+        final String utilddl = IOUtils.toString(MysqlTestingHelper.class.getResourceAsStream("/com/ning/billing/util/ddl.sql"));
 
         helper.startMysql();
         helper.initDb(paymentddl);
+        helper.initDb(utilddl);
     }
 
     @AfterClass(groups = { "slow", "database" })
diff --git a/payment/src/test/java/com/ning/billing/payment/MockInvoice.java b/payment/src/test/java/com/ning/billing/payment/MockInvoice.java
new file mode 100644
index 0000000..20c0446
--- /dev/null
+++ b/payment/src/test/java/com/ning/billing/payment/MockInvoice.java
@@ -0,0 +1,237 @@
+/*
+ * 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.payment;
+
+import java.math.BigDecimal;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.UUID;
+
+import javax.annotation.Nullable;
+
+import com.ning.billing.util.dao.ObjectType;
+import org.joda.time.DateTime;
+
+import com.ning.billing.catalog.api.Currency;
+import com.ning.billing.invoice.api.Invoice;
+import com.ning.billing.invoice.api.InvoiceItem;
+import com.ning.billing.invoice.api.InvoicePayment;
+import com.ning.billing.util.callcontext.CallContext;
+import com.ning.billing.util.customfield.CustomField;
+import com.ning.billing.util.entity.ExtendedEntityBase;
+
+public class MockInvoice extends ExtendedEntityBase implements Invoice {
+    private final List<InvoiceItem> invoiceItems = new ArrayList<InvoiceItem>();
+    private final List<InvoicePayment> payments = new ArrayList<InvoicePayment>();
+    private final UUID accountId;
+    private final Integer invoiceNumber;
+    private final DateTime invoiceDate;
+    private final DateTime targetDate;
+    private final Currency currency;
+    private final boolean migrationInvoice;
+
+    // used to create a new invoice
+    public MockInvoice(UUID accountId, DateTime invoiceDate, DateTime targetDate, Currency currency) {
+        this(UUID.randomUUID(), accountId, null, invoiceDate, targetDate, currency, false);
+    }
+
+    // used to hydrate invoice from persistence layer
+    public MockInvoice(UUID invoiceId, UUID accountId, @Nullable Integer invoiceNumber, DateTime invoiceDate,
+                          DateTime targetDate, Currency currency, boolean isMigrationInvoice) {
+        super(invoiceId);
+        this.accountId = accountId;
+        this.invoiceNumber = invoiceNumber;
+        this.invoiceDate = invoiceDate;
+        this.targetDate = targetDate;
+        this.currency = currency;
+        this.migrationInvoice = isMigrationInvoice;
+    }
+
+    @Override
+    public boolean addInvoiceItem(final InvoiceItem item) {
+        return invoiceItems.add(item);
+    }
+
+    @Override
+    public boolean addInvoiceItems(final List<InvoiceItem> items) {
+        return this.invoiceItems.addAll(items);
+    }
+
+    @Override
+    public List<InvoiceItem> getInvoiceItems() {
+        return invoiceItems;
+    }
+
+    @Override
+    public <T extends InvoiceItem> List<InvoiceItem> getInvoiceItems(Class<T> clazz) {
+        List<InvoiceItem> results = new ArrayList<InvoiceItem>();
+        for (InvoiceItem item : invoiceItems) {
+            if ( clazz.isInstance(item) ) {
+                results.add(item);
+            }
+        }
+        return results;
+    }
+
+    @Override
+    public int getNumberOfItems() {
+        return invoiceItems.size();
+    }
+
+    @Override
+    public boolean addPayment(final InvoicePayment payment) {
+        return payments.add(payment);
+    }
+
+    @Override
+    public boolean addPayments(final List<InvoicePayment> payments) {
+        return this.payments.addAll(payments);
+    }
+
+    @Override
+    public List<InvoicePayment> getPayments() {
+        return payments;
+    }
+
+    @Override
+    public int getNumberOfPayments() {
+        return payments.size();
+    }
+
+    @Override
+    public UUID getId() {
+        return id;
+    }
+
+    @Override
+    public UUID getAccountId() {
+        return accountId;
+    }
+
+    /**
+     * null until retrieved from the database
+     * @return the invoice number
+     */
+    @Override
+    public Integer getInvoiceNumber() {
+        return invoiceNumber;
+    }
+
+    @Override
+    public DateTime getInvoiceDate() {
+        return invoiceDate;
+    }
+
+    @Override
+    public DateTime getTargetDate() {
+        return targetDate;
+    }
+
+    @Override
+    public Currency getCurrency() {
+        return currency;
+    }
+    
+    @Override
+    public boolean isMigrationInvoice() {
+		return migrationInvoice;
+	}
+
+	@Override
+    public DateTime getLastPaymentAttempt() {
+        DateTime lastPaymentAttempt = null;
+
+        for (final InvoicePayment paymentAttempt : payments) {
+            DateTime paymentAttemptDate = paymentAttempt.getPaymentAttemptDate();
+            if (lastPaymentAttempt == null) {
+                lastPaymentAttempt = paymentAttemptDate;
+            }
+
+            if (lastPaymentAttempt.isBefore(paymentAttemptDate)) {
+                lastPaymentAttempt = paymentAttemptDate;
+            }
+        }
+
+        return lastPaymentAttempt;
+    }
+
+    @Override
+    public BigDecimal getAmountPaid() {
+        BigDecimal amountPaid = BigDecimal.ZERO;
+        for (final InvoicePayment payment : payments) {
+            if (payment.getAmount() != null) {
+                amountPaid = amountPaid.add(payment.getAmount());
+            }
+        }
+        return amountPaid;
+    }
+
+    @Override
+    public BigDecimal getTotalAmount() {
+        BigDecimal result = BigDecimal.ZERO;
+    
+        for(InvoiceItem i : invoiceItems) {
+            result = result.add(i.getAmount());
+        }
+        return result;
+    }
+
+    @Override
+    public BigDecimal getBalance() {
+        return getTotalAmount().subtract(getAmountPaid());
+    }
+
+    @Override
+    public boolean isDueForPayment(final DateTime targetDate, final int numberOfDays) {
+        if (getTotalAmount().compareTo(BigDecimal.ZERO) == 0) {
+            return false;
+        }
+
+        DateTime lastPaymentAttempt = getLastPaymentAttempt();
+        if (lastPaymentAttempt == null) {
+            return true;
+        }
+
+        return !lastPaymentAttempt.plusDays(numberOfDays).isAfter(targetDate);
+    }
+
+    @Override
+    public String toString() {
+        return "DefaultInvoice [items=" + invoiceItems + ", payments=" + payments + ", id=" + id + ", accountId=" + accountId + ", invoiceDate=" + invoiceDate + ", targetDate=" + targetDate + ", currency=" + currency + ", amountPaid=" + getAmountPaid() + ", lastPaymentAttempt=" + getLastPaymentAttempt() + "]";
+    }
+
+    @Override
+    public ObjectType getObjectType() {
+        return ObjectType.RECURRING_INVOICE_ITEM;
+    }
+
+    @Override
+    public void saveFieldValue(String fieldName, String fieldValue, CallContext context) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void saveFields(List<CustomField> fields, CallContext context) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void clearPersistedFields(CallContext context) {
+        throw new UnsupportedOperationException();
+    }
+}
+
diff --git a/payment/src/test/java/com/ning/billing/payment/MockInvoiceCreationEvent.java b/payment/src/test/java/com/ning/billing/payment/MockInvoiceCreationEvent.java
new file mode 100644
index 0000000..9a700cd
--- /dev/null
+++ b/payment/src/test/java/com/ning/billing/payment/MockInvoiceCreationEvent.java
@@ -0,0 +1,157 @@
+/*
+ * 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.payment;
+
+import java.math.BigDecimal;
+import java.util.UUID;
+
+import org.codehaus.jackson.annotate.JsonCreator;
+import org.codehaus.jackson.annotate.JsonIgnore;
+import org.codehaus.jackson.annotate.JsonProperty;
+import org.joda.time.DateTime;
+
+import com.ning.billing.catalog.api.Currency;
+import com.ning.billing.invoice.api.InvoiceCreationEvent;
+import com.ning.billing.util.bus.BusEvent.BusEventType;
+
+public class MockInvoiceCreationEvent implements InvoiceCreationEvent {
+	
+    private final UUID invoiceId;
+    private final UUID accountId;
+    private final BigDecimal amountOwed;
+    private final Currency currency;
+    private final DateTime invoiceCreationDate;
+    private final UUID userToken;
+
+    @JsonCreator
+    public MockInvoiceCreationEvent(@JsonProperty("invoiceId") UUID invoiceId,
+            @JsonProperty("accountId") UUID accountId,
+            @JsonProperty("amountOwed") BigDecimal amountOwed,
+            @JsonProperty("currency") Currency currency,
+            @JsonProperty("invoiceCreationDate") DateTime invoiceCreationDate,
+            @JsonProperty("userToken") UUID userToken) {
+        this.invoiceId = invoiceId;
+        this.accountId = accountId;
+        this.amountOwed = amountOwed;
+        this.currency = currency;
+        this.invoiceCreationDate = invoiceCreationDate;
+        this.userToken = userToken;
+    }
+
+    @JsonIgnore
+	@Override
+	public BusEventType getBusEventType() {
+		return BusEventType.INVOICE_CREATION;
+	}
+
+	@Override
+	public UUID getUserToken() {
+		return userToken;
+	}
+
+    @Override
+    public UUID getInvoiceId() {
+        return invoiceId;
+    }
+
+    @Override
+    public UUID getAccountId() {
+        return accountId;
+    }
+
+    @Override
+    public BigDecimal getAmountOwed() {
+        return amountOwed;
+    }
+
+    @Override
+    public Currency getCurrency() {
+        return currency;
+    }
+
+    @Override
+    public DateTime getInvoiceCreationDate() {
+        return invoiceCreationDate;
+    }
+
+    @Override
+    public String toString() {
+        return "DefaultInvoiceCreationNotification [invoiceId=" + invoiceId + ", accountId=" + accountId + ", amountOwed=" + amountOwed + ", currency=" + currency + ", invoiceCreationDate=" + invoiceCreationDate + "]";
+    }
+
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result
+                + ((accountId == null) ? 0 : accountId.hashCode());
+        result = prime * result
+                + ((amountOwed == null) ? 0 : amountOwed.hashCode());
+        result = prime * result
+                + ((currency == null) ? 0 : currency.hashCode());
+        result = prime
+                * result
+                + ((invoiceCreationDate == null) ? 0 : invoiceCreationDate
+                        .hashCode());
+        result = prime * result
+                + ((invoiceId == null) ? 0 : invoiceId.hashCode());
+        result = prime * result
+                + ((userToken == null) ? 0 : userToken.hashCode());
+        return result;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj)
+            return true;
+        if (obj == null)
+            return false;
+        if (getClass() != obj.getClass())
+            return false;
+        MockInvoiceCreationEvent other = (MockInvoiceCreationEvent) obj;
+        if (accountId == null) {
+            if (other.accountId != null)
+                return false;
+        } else if (!accountId.equals(other.accountId))
+            return false;
+        if (amountOwed == null) {
+            if (other.amountOwed != null)
+                return false;
+        } else if (!amountOwed.equals(other.amountOwed))
+            return false;
+        if (currency != other.currency)
+            return false;
+        if (invoiceCreationDate == null) {
+            if (other.invoiceCreationDate != null)
+                return false;
+        } else if (invoiceCreationDate.compareTo(other.invoiceCreationDate) != 0)
+            return false;
+        if (invoiceId == null) {
+            if (other.invoiceId != null)
+                return false;
+        } else if (!invoiceId.equals(other.invoiceId))
+            return false;
+        if (userToken == null) {
+            if (other.userToken != null)
+                return false;
+        } else if (!userToken.equals(other.userToken))
+            return false;
+        return true;
+    }
+    
+    
+}
diff --git a/payment/src/test/java/com/ning/billing/payment/MockRecurringInvoiceItem.java b/payment/src/test/java/com/ning/billing/payment/MockRecurringInvoiceItem.java
new file mode 100644
index 0000000..93c90fa
--- /dev/null
+++ b/payment/src/test/java/com/ning/billing/payment/MockRecurringInvoiceItem.java
@@ -0,0 +1,264 @@
+/*
+ * 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.payment;
+
+import com.ning.billing.catalog.api.Currency;
+import com.ning.billing.invoice.api.InvoiceItem;
+import com.ning.billing.util.entity.EntityBase;
+
+import org.apache.commons.lang.NotImplementedException;
+import org.joda.time.DateTime;
+
+import java.math.BigDecimal;
+import java.util.UUID;
+
+import javax.annotation.Nullable;
+
+public class MockRecurringInvoiceItem extends EntityBase implements InvoiceItem {
+    private final BigDecimal rate;
+    private final UUID reversedItemId;
+    protected final UUID invoiceId;
+    protected final UUID accountId;
+    protected final UUID subscriptionId;
+    protected final UUID bundleId;
+    protected final String planName;
+    protected final String phaseName;
+    protected final DateTime startDate;
+    protected final DateTime endDate;
+    protected final BigDecimal amount;
+    protected final Currency currency;
+
+
+    public MockRecurringInvoiceItem(UUID invoiceId, UUID accountId, UUID bundleId, UUID subscriptionId, String planName, String phaseName,
+                                DateTime startDate, DateTime endDate,
+                                BigDecimal amount, BigDecimal rate,
+                                Currency currency) { 
+        this(invoiceId, accountId, bundleId, subscriptionId, planName, phaseName, startDate, endDate, amount, currency, rate, null);
+
+    }
+
+    public MockRecurringInvoiceItem(UUID invoiceId, UUID accountId, UUID bundleId, UUID subscriptionId, String planName, String phaseName,
+                                DateTime startDate, DateTime endDate,
+                                BigDecimal amount, BigDecimal rate,
+                                Currency currency, UUID reversedItemId) {
+        this(invoiceId, accountId, bundleId, subscriptionId, planName, phaseName, startDate, endDate,
+                amount, currency, rate, reversedItemId);
+    }
+
+    public MockRecurringInvoiceItem(UUID id, UUID invoiceId, UUID accountId, UUID bundleId, UUID subscriptionId, String planName, String phaseName,
+                                DateTime startDate, DateTime endDate,
+                                BigDecimal amount, BigDecimal rate,
+                                Currency currency) {
+        this(id, invoiceId, accountId, bundleId, subscriptionId, planName, phaseName, startDate, endDate, amount, currency, rate, null);
+
+    }
+
+    public MockRecurringInvoiceItem(UUID id, UUID invoiceId, UUID accountId, UUID bundleId, UUID subscriptionId, String planName, String phaseName,
+                                DateTime startDate, DateTime endDate,
+                                BigDecimal amount, BigDecimal rate,
+                                Currency currency, UUID reversedItemId) {
+        this(id, invoiceId, accountId, bundleId, subscriptionId, planName, phaseName, startDate, endDate, amount, currency, rate, reversedItemId);
+    }
+    public MockRecurringInvoiceItem(UUID invoiceId, UUID accountId, UUID bundleId, UUID subscriptionId, String planName, String phaseName,
+            DateTime startDate, DateTime endDate, BigDecimal amount, Currency currency, BigDecimal rate, UUID reversedItemId){
+        this(UUID.randomUUID(), invoiceId, accountId, bundleId, subscriptionId, planName, phaseName,
+                startDate, endDate, amount, currency, rate, reversedItemId);
+    }
+
+
+    public MockRecurringInvoiceItem(UUID id, UUID invoiceId, UUID accountId, @Nullable UUID bundleId, @Nullable UUID subscriptionId, String planName, String phaseName,
+            DateTime startDate, DateTime endDate, BigDecimal amount, Currency currency,
+            BigDecimal rate, UUID reversedItemId) {
+        super(id);
+        this.invoiceId = invoiceId;
+        this.accountId = accountId;
+        this.subscriptionId = subscriptionId;
+        this.bundleId = bundleId;
+        this.planName = planName;
+        this.phaseName = phaseName;
+        this.startDate = startDate;
+        this.endDate = endDate;
+        this.amount = amount;
+        this.currency = currency;
+        this.rate = rate;
+        this.reversedItemId = reversedItemId;
+    }
+
+    @Override
+    public UUID getId() {
+        return id;
+    }
+
+    @Override
+    public UUID getInvoiceId() {
+        return invoiceId;
+    }
+
+    @Override
+    public UUID getBundleId() {
+        return bundleId;
+    }
+    
+    public UUID getAccountId() {
+        return accountId;
+    }
+
+    @Override
+    public UUID getSubscriptionId() {
+        return subscriptionId;
+    }
+
+    @Override
+    public String getPlanName() {
+        return planName;
+    }
+
+    @Override
+    public String getPhaseName() {
+        return phaseName;
+    }
+
+    @Override
+    public BigDecimal getAmount() {
+        return amount;
+    }
+
+    @Override
+    public DateTime getStartDate() {
+        return startDate;
+    }
+
+    @Override
+    public DateTime getEndDate() {
+        return endDate;
+    }
+
+    @Override
+    public Currency getCurrency() {
+        return currency;
+    }
+    @Override
+    public InvoiceItem asReversingItem() {
+        throw new NotImplementedException();
+    }
+
+    @Override
+    public String getDescription() {
+        return String.format("%s from %s to %s", phaseName, startDate.toString(), endDate.toString());
+    }
+
+    public UUID getReversedItemId() {
+        return reversedItemId;
+    }
+
+    public boolean reversesItem() {
+        return (reversedItemId != null);
+    }
+
+    public BigDecimal getRate() {
+        return rate;
+    }
+
+    @Override
+    public int compareTo(InvoiceItem item) {
+        if (item == null) {
+            return -1;
+        }
+        if (!(item instanceof MockRecurringInvoiceItem)) {
+            return -1;
+        }
+
+        MockRecurringInvoiceItem that = (MockRecurringInvoiceItem) item;
+        int compareAccounts = getAccountId().compareTo(that.getAccountId());
+        if (compareAccounts == 0 && bundleId != null) {
+            int compareBundles = getBundleId().compareTo(that.getBundleId());
+            if (compareBundles == 0 && subscriptionId != null) {
+
+                int compareSubscriptions = getSubscriptionId().compareTo(that.getSubscriptionId());
+                if (compareSubscriptions == 0) {
+                    int compareStartDates = getStartDate().compareTo(that.getStartDate());
+                    if (compareStartDates == 0) {
+                        return getEndDate().compareTo(that.getEndDate());
+                    } else {
+                        return compareStartDates;
+                    }
+                } else {
+                    return compareSubscriptions;
+                }
+            } else {
+                return compareBundles;
+            }
+        } else {
+            return compareAccounts;
+        }
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+
+        MockRecurringInvoiceItem that = (MockRecurringInvoiceItem) o;
+
+        if (accountId.compareTo(that.accountId) != 0) return false;
+        if (amount.compareTo(that.amount) != 0) return false;
+        if (currency != that.currency) return false;
+        if (startDate.compareTo(that.startDate) != 0) return false;
+        if (endDate.compareTo(that.endDate) != 0) return false;
+        if (!phaseName.equals(that.phaseName)) return false;
+        if (!planName.equals(that.planName)) return false;
+        if (rate.compareTo(that.rate) != 0) return false;
+        if (reversedItemId != null ? !reversedItemId.equals(that.reversedItemId) : that.reversedItemId != null)
+            return false;
+        if (subscriptionId != null ? !subscriptionId.equals(that.subscriptionId) : that.subscriptionId != null)
+            return false;
+        if (bundleId != null ? !bundleId.equals(that.bundleId) : that.bundleId != null)
+            return false;
+
+        return true;
+    }
+
+    @Override
+    public int hashCode() {
+        int result = accountId.hashCode();
+        result = 31 * result + (subscriptionId != null ? subscriptionId.hashCode() : 0);
+        result = 31 * result + (bundleId != null ? bundleId.hashCode() : 0);
+        result = 31 * result + planName.hashCode();
+        result = 31 * result + phaseName.hashCode();
+        result = 31 * result + startDate.hashCode();
+        result = 31 * result + endDate.hashCode();
+        result = 31 * result + amount.hashCode();
+        result = 31 * result + rate.hashCode();
+        result = 31 * result + currency.hashCode();
+        result = 31 * result + (reversedItemId != null ? reversedItemId.hashCode() : 0);
+        return result;
+    }
+
+    @Override
+    public String toString() {
+        StringBuilder sb = new StringBuilder();
+
+        sb.append(phaseName).append(", ");
+        sb.append(startDate.toString()).append(", ");
+        sb.append(endDate.toString()).append(", ");
+        sb.append(amount.toString()).append(", ");
+        sb.append("subscriptionId = ").append(subscriptionId == null ? null : subscriptionId.toString()).append(", ");
+        sb.append("bundleId = ").append(bundleId == null ? null : bundleId.toString()).append(", ");
+
+        return sb.toString();
+    }
+}
diff --git a/payment/src/test/java/com/ning/billing/payment/provider/MockPaymentProviderPlugin.java b/payment/src/test/java/com/ning/billing/payment/provider/MockPaymentProviderPlugin.java
index e6cda1a..f3cb154 100644
--- a/payment/src/test/java/com/ning/billing/payment/provider/MockPaymentProviderPlugin.java
+++ b/payment/src/test/java/com/ning/billing/payment/provider/MockPaymentProviderPlugin.java
@@ -44,7 +44,7 @@ import com.ning.billing.util.clock.Clock;
 
 public class MockPaymentProviderPlugin implements PaymentProviderPlugin {
     private final AtomicBoolean makeNextInvoiceFail = new AtomicBoolean(false);
-    private final Map<String, PaymentInfoEvent> payments = new ConcurrentHashMap<String, PaymentInfoEvent>();
+    private final Map<UUID, PaymentInfoEvent> payments = new ConcurrentHashMap<UUID, PaymentInfoEvent>();
     private final Map<String, PaymentProviderAccount> accounts = new ConcurrentHashMap<String, PaymentProviderAccount>();
     private final Map<String, PaymentMethodInfo> paymentMethods = new ConcurrentHashMap<String, PaymentMethodInfo>();
     private final Clock clock;
@@ -64,7 +64,7 @@ public class MockPaymentProviderPlugin implements PaymentProviderPlugin {
             return Either.left((PaymentErrorEvent) new DefaultPaymentErrorEvent("unknown", "test error", account.getId(), invoice.getId(), null));
         }
         else {
-            PaymentInfoEvent payment = new DefaultPaymentInfoEvent.Builder().setPaymentId(UUID.randomUUID().toString())
+            PaymentInfoEvent payment = new DefaultPaymentInfoEvent.Builder().setId(UUID.randomUUID())
                                                  .setAmount(invoice.getBalance())
                                                  .setStatus("Processed")
                                                  .setBankIdentificationNumber("1234")
@@ -73,7 +73,7 @@ public class MockPaymentProviderPlugin implements PaymentProviderPlugin {
                                                  .setReferenceId("12345")
                                                  .setType("Electronic")
                                                  .build();
-            payments.put(payment.getPaymentId(), payment);
+            payments.put(payment.getId(), payment);
             return Either.right(payment);
         }
     }
diff --git a/payment/src/test/java/com/ning/billing/payment/setup/PaymentTestModuleWithEmbeddedDb.java b/payment/src/test/java/com/ning/billing/payment/setup/PaymentTestModuleWithEmbeddedDb.java
index cf56d78..da38b34 100644
--- a/payment/src/test/java/com/ning/billing/payment/setup/PaymentTestModuleWithEmbeddedDb.java
+++ b/payment/src/test/java/com/ning/billing/payment/setup/PaymentTestModuleWithEmbeddedDb.java
@@ -20,8 +20,6 @@ import org.apache.commons.collections.MapUtils;
 
 import com.google.common.collect.ImmutableMap;
 import com.google.inject.Provider;
-import com.ning.billing.account.api.AccountUserApi;
-import com.ning.billing.account.api.user.DefaultAccountUserApi;
 import com.ning.billing.config.PaymentConfig;
 import com.ning.billing.junction.api.BillingApi;
 import com.ning.billing.mock.BrainDeadProxyFactory;
diff --git a/payment/src/test/java/com/ning/billing/payment/setup/PaymentTestModuleWithMocks.java b/payment/src/test/java/com/ning/billing/payment/setup/PaymentTestModuleWithMocks.java
index 8ec8862..14f2ab9 100644
--- a/payment/src/test/java/com/ning/billing/payment/setup/PaymentTestModuleWithMocks.java
+++ b/payment/src/test/java/com/ning/billing/payment/setup/PaymentTestModuleWithMocks.java
@@ -16,8 +16,6 @@
 
 package com.ning.billing.payment.setup;
 
-import com.ning.billing.invoice.api.test.DefaultInvoiceTestApi;
-import com.ning.billing.invoice.api.test.InvoiceTestApi;
 import org.apache.commons.collections.MapUtils;
 
 import com.google.common.collect.ImmutableMap;
@@ -25,14 +23,14 @@ import com.google.inject.Provider;
 import com.ning.billing.config.PaymentConfig;
 import com.ning.billing.junction.api.BillingApi;
 import com.ning.billing.mock.BrainDeadProxyFactory;
-import com.ning.billing.mock.BrainDeadProxyFactory.ZombieControl;
+import com.ning.billing.mock.glue.MockInvoiceModule;
+import com.ning.billing.mock.glue.MockNotificationQueueModule;
+import com.ning.billing.mock.glue.TestDbiModule;
 import com.ning.billing.payment.dao.MockPaymentDao;
 import com.ning.billing.payment.dao.PaymentDao;
 import com.ning.billing.payment.provider.MockPaymentProviderPluginModule;
-import com.ning.billing.util.bus.Bus;
-import com.ning.billing.util.bus.InMemoryBus;
-import com.ning.billing.util.notificationq.MockNotificationQueueService;
-import com.ning.billing.util.notificationq.NotificationQueueService;
+import com.ning.billing.util.glue.BusModule;
+import com.ning.billing.util.glue.BusModule.BusType;
 
 public class PaymentTestModuleWithMocks extends PaymentModule {
 	public static class MockProvider implements Provider<BillingApi> {
@@ -62,9 +60,9 @@ public class PaymentTestModuleWithMocks extends PaymentModule {
     @Override
     protected void configure() {
         super.configure();
-        bind(Bus.class).to(InMemoryBus.class).asEagerSingleton();
-        bind(InvoiceTestApi.class).to(DefaultInvoiceTestApi.class).asEagerSingleton();
-
-        bind(NotificationQueueService.class).to(MockNotificationQueueService.class).asEagerSingleton();
+        install(new BusModule(BusType.MEMORY));
+        install(new MockNotificationQueueModule());
+        install(new MockInvoiceModule());
+        install(new TestDbiModule());
     }
 }
diff --git a/payment/src/test/java/com/ning/billing/payment/TestHelper.java b/payment/src/test/java/com/ning/billing/payment/TestHelper.java
index 93c7080..33dc142 100644
--- a/payment/src/test/java/com/ning/billing/payment/TestHelper.java
+++ b/payment/src/test/java/com/ning/billing/payment/TestHelper.java
@@ -16,24 +16,23 @@
 
 package com.ning.billing.payment;
 
-import java.math.BigDecimal;
 import java.util.UUID;
 
-import com.ning.billing.invoice.api.test.InvoiceTestApi;
-import com.ning.billing.mock.BrainDeadProxyFactory;
 import org.apache.commons.lang.RandomStringUtils;
 import org.joda.time.DateTime;
-import org.joda.time.DateTimeZone;
 
 import com.google.inject.Inject;
 import com.ning.billing.account.api.Account;
 import com.ning.billing.account.api.AccountUserApi;
 import com.ning.billing.catalog.api.Currency;
 import com.ning.billing.invoice.api.Invoice;
+import com.ning.billing.invoice.api.InvoiceCreationEvent;
 import com.ning.billing.invoice.api.InvoiceItem;
-import com.ning.billing.invoice.model.DefaultInvoice;
-import com.ning.billing.invoice.model.RecurringInvoiceItem;
+import com.ning.billing.invoice.api.InvoicePaymentApi;
+import com.ning.billing.mock.BrainDeadProxyFactory;
 import com.ning.billing.mock.BrainDeadProxyFactory.ZombieControl;
+import com.ning.billing.util.bus.Bus;
+import com.ning.billing.util.bus.Bus.EventBusException;
 import com.ning.billing.util.callcontext.CallContext;
 import com.ning.billing.util.callcontext.CallContextFactory;
 import com.ning.billing.util.callcontext.CallOrigin;
@@ -42,13 +41,15 @@ import com.ning.billing.util.entity.EntityPersistenceException;
 
 public class TestHelper {
     protected final AccountUserApi accountUserApi;
-    protected final InvoiceTestApi invoiceTestApi;
+    protected final InvoicePaymentApi invoicePaymentApi;
     private final CallContext context;
+    private final Bus eventBus;
 
     @Inject
-    public TestHelper(CallContextFactory factory, AccountUserApi accountUserApi, InvoiceTestApi invoiceTestApi) {
+    public TestHelper(CallContextFactory factory, AccountUserApi accountUserApi, InvoicePaymentApi invoicePaymentApi, Bus eventBus) {
+        this.eventBus = eventBus;
         this.accountUserApi = accountUserApi;
-        this.invoiceTestApi = invoiceTestApi;
+        this.invoicePaymentApi = invoicePaymentApi;
         context = factory.createCallContext("Princess Buttercup", CallOrigin.TEST, UserType.TEST);
     }
 
@@ -70,13 +71,13 @@ public class TestHelper {
     public Invoice createTestInvoice(Account account,
                                      DateTime targetDate,
                                      Currency currency,
-                                     InvoiceItem... items) {
-        Invoice invoice = new DefaultInvoice(account.getId(), new DateTime(), targetDate, currency);
+                                     InvoiceItem... items) throws EventBusException {
+        Invoice invoice = new MockInvoice(account.getId(), new DateTime(), targetDate, currency);
 
         for (InvoiceItem item : items) {
-            if (item instanceof RecurringInvoiceItem) {
-                RecurringInvoiceItem recurringInvoiceItem = (RecurringInvoiceItem) item;
-                invoice.addInvoiceItem(new RecurringInvoiceItem(invoice.getId(),
+            if (item instanceof MockRecurringInvoiceItem) {
+                MockRecurringInvoiceItem recurringInvoiceItem = (MockRecurringInvoiceItem) item;
+                invoice.addInvoiceItem(new MockRecurringInvoiceItem(invoice.getId(),
                                                                account.getId(),
                                                                recurringInvoiceItem.getBundleId(),
                                                                recurringInvoiceItem.getSubscriptionId(),
@@ -90,21 +91,15 @@ public class TestHelper {
             }
         }
 
-        invoiceTestApi.create(invoice, context);
-        return invoice;
-    }
-
-    public Invoice createTestInvoice(Account account) {
-        final DateTime now = new DateTime(DateTimeZone.UTC);
-        final UUID subscriptionId = UUID.randomUUID();
-        final UUID bundleId = UUID.randomUUID();
-        final BigDecimal amount = new BigDecimal("10.00");
+ //       invoiceTestApi.create(invoice, context);
+        ((ZombieControl)invoicePaymentApi).addResult("getInvoice", invoice);
+        InvoiceCreationEvent event = new MockInvoiceCreationEvent(invoice.getId(), invoice.getAccountId(),
+                invoice.getBalance(), invoice.getCurrency(),
+                invoice.getInvoiceDate(),
+                context.getUserToken());
         
-        final InvoiceItem item = new RecurringInvoiceItem(null, account.getId(), bundleId, subscriptionId, "test plan", "test phase", now, now.plusMonths(1),
-                amount, new BigDecimal("1.0"), Currency.USD);
-
-
-        return createTestInvoice(account, now, Currency.USD, item);
+        eventBus.post(event);
+        return invoice;
     }
 
     public Account createTestAccount(String email) {
@@ -121,6 +116,7 @@ public class TestHelper {
         zombie.addResult("getEmail", email);
         zombie.addResult("getCurrency", Currency.USD);
         zombie.addResult("getBillCycleDay", 1);
+        zombie.addResult("getPaymentProviderName", "");
 
         return account;
     }
diff --git a/payment/src/test/java/com/ning/billing/payment/TestRetryService.java b/payment/src/test/java/com/ning/billing/payment/TestRetryService.java
index 8b7720a..2c519d0 100644
--- a/payment/src/test/java/com/ning/billing/payment/TestRetryService.java
+++ b/payment/src/test/java/com/ning/billing/payment/TestRetryService.java
@@ -40,17 +40,16 @@ import org.testng.annotations.Test;
 
 import com.google.inject.Inject;
 import com.ning.billing.account.api.Account;
-import com.ning.billing.account.glue.AccountModuleWithMocks;
 import com.ning.billing.catalog.api.Currency;
 import com.ning.billing.config.PaymentConfig;
 import com.ning.billing.invoice.api.Invoice;
-import com.ning.billing.invoice.glue.InvoiceModuleWithMocks;
-import com.ning.billing.invoice.model.RecurringInvoiceItem;
+import com.ning.billing.invoice.api.InvoicePaymentApi;
+import com.ning.billing.mock.BrainDeadProxyFactory;
+import com.ning.billing.mock.BrainDeadProxyFactory.ZombieControl;
 import com.ning.billing.mock.glue.MockJunctionModule;
-import com.ning.billing.payment.api.Either;
 import com.ning.billing.payment.api.PaymentApi;
+import com.ning.billing.payment.api.PaymentApiException;
 import com.ning.billing.payment.api.PaymentAttempt;
-import com.ning.billing.payment.api.PaymentErrorEvent;
 import com.ning.billing.payment.api.PaymentInfoEvent;
 import com.ning.billing.payment.api.PaymentStatus;
 import com.ning.billing.payment.dao.PaymentDao;
@@ -60,11 +59,13 @@ import com.ning.billing.payment.setup.PaymentTestModuleWithMocks;
 import com.ning.billing.util.bus.Bus;
 import com.ning.billing.util.clock.Clock;
 import com.ning.billing.util.clock.ClockMock;
+import com.ning.billing.util.clock.MockClockModule;
+import com.ning.billing.util.glue.CallContextModule;
 import com.ning.billing.util.notificationq.MockNotificationQueue;
 import com.ning.billing.util.notificationq.Notification;
 import com.ning.billing.util.notificationq.NotificationQueueService;
 
-@Guice(modules = { PaymentTestModuleWithMocks.class, AccountModuleWithMocks.class, InvoiceModuleWithMocks.class, MockJunctionModule.class })
+@Guice(modules = { PaymentTestModuleWithMocks.class, MockClockModule.class, MockJunctionModule.class, CallContextModule.class })
 @Test(groups = "fast")
 public class TestRetryService {
     @Inject
@@ -74,6 +75,8 @@ public class TestRetryService {
     @Inject
     private PaymentApi paymentApi;
     @Inject
+    private InvoicePaymentApi invoicePaymentApi;
+    @Inject
     private TestHelper testHelper;
     @Inject
     private PaymentProviderPluginRegistry registry;
@@ -104,6 +107,8 @@ public class TestRetryService {
         mockPaymentProviderPlugin = (MockPaymentProviderPlugin)registry.getPlugin(null);
         mockNotificationQueue = (MockNotificationQueue)notificationQueueService.getNotificationQueue(RetryService.SERVICE_NAME, RetryService.QUEUE_NAME);
         context = new DefaultCallContext("RetryServiceTests", CallOrigin.INTERNAL, UserType.TEST, clock);
+        ((ZombieControl)invoicePaymentApi).addResult("notifyOfPaymentAttempt", BrainDeadProxyFactory.ZOMBIE_VOID);
+
     }
 
     @AfterMethod(alwaysRun = true)
@@ -122,7 +127,7 @@ public class TestRetryService {
 
         final DateTime startDate = clock.getUTCNow();
         final DateTime endDate = startDate.plusMonths(1);
-        invoice.addInvoiceItem(new RecurringInvoiceItem(invoice.getId(),
+        invoice.addInvoiceItem(new MockRecurringInvoiceItem(invoice.getId(),
                                                        account.getId(),
                                                        subscriptionId,
                                                        bundleId,
@@ -134,11 +139,13 @@ public class TestRetryService {
                                                        Currency.USD));
 
         mockPaymentProviderPlugin.makeNextInvoiceFail();
-
-        List<Either<PaymentErrorEvent, PaymentInfoEvent>> results = paymentApi.createPayment(account.getExternalKey(), Arrays.asList(invoice.getId().toString()), context);
-
-        assertEquals(results.size(), 1);
-        assertTrue(results.get(0).isLeft());
+        boolean failed = false;
+        try {
+            paymentApi.createPayment(account.getExternalKey(), Arrays.asList(invoice.getId().toString()), context);
+        } catch (PaymentApiException e) {
+            failed = true;
+        }
+        assertTrue(failed);
 
         List<Notification> pendingNotifications = mockNotificationQueue.getPendingEvents();
 
@@ -165,7 +172,7 @@ public class TestRetryService {
 
         final DateTime now = clock.getUTCNow();
 
-        invoice.addInvoiceItem(new RecurringInvoiceItem(invoice.getId(),
+        invoice.addInvoiceItem(new MockRecurringInvoiceItem(invoice.getId(),
                                                        account.getId(),
                                                        subscriptionId,
                                                        bundleId,
@@ -198,7 +205,7 @@ public class TestRetryService {
         assertEquals(paymentInfo.getStatus(), PaymentStatus.Processed.toString());
 
         List<PaymentAttempt> updatedAttempts = paymentApi.getPaymentAttemptsForInvoiceId(invoice.getId().toString());
-        assertEquals(paymentInfo.getPaymentId(), updatedAttempts.get(0).getPaymentId());
+        assertEquals(paymentInfo.getId(), updatedAttempts.get(0).getPaymentId());
 
     }
 }

pom.xml 12(+12 -0)

diff --git a/pom.xml b/pom.xml
index 02b1aa6..7fe6a40 100644
--- a/pom.xml
+++ b/pom.xml
@@ -176,6 +176,18 @@
             </dependency>
             <dependency>
                 <groupId>com.ning.billing</groupId>
+                <artifactId>killbill-overdue</artifactId>
+                <version>${project.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>com.ning.billing</groupId>
+                <artifactId>killbill-overdue</artifactId>
+                <version>${project.version}</version>
+                <type>test-jar</type>
+                <scope>test</scope>
+            </dependency>
+            <dependency>
+                <groupId>com.ning.billing</groupId>
                 <artifactId>killbill-util</artifactId>
                 <version>${project.version}</version>
                 <type>test-jar</type>

server/pom.xml 12(+8 -4)

diff --git a/server/pom.xml b/server/pom.xml
index 4bc0388..e740c10 100644
--- a/server/pom.xml
+++ b/server/pom.xml
@@ -155,10 +155,14 @@
 		</dependency>
 
 		<!-- FROM MASTER POM / LIBRARY -->
-		<dependency>
-			<groupId>com.ning.billing</groupId>
-			<artifactId>killbill-api</artifactId>
-		</dependency>
+        <dependency>
+            <groupId>com.ning.billing</groupId>
+            <artifactId>killbill-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.ning.billing</groupId>
+            <artifactId>killbill-account</artifactId>
+        </dependency>
 		<dependency>
 			<groupId>com.ning.billing</groupId>
 			<artifactId>killbill-jaxrs</artifactId>
diff --git a/server/src/main/java/com/ning/billing/server/listeners/KillbillGuiceListener.java b/server/src/main/java/com/ning/billing/server/listeners/KillbillGuiceListener.java
index 3ec9e22..227e94a 100644
--- a/server/src/main/java/com/ning/billing/server/listeners/KillbillGuiceListener.java
+++ b/server/src/main/java/com/ning/billing/server/listeners/KillbillGuiceListener.java
@@ -26,11 +26,11 @@ import com.ning.billing.util.bus.BusService;
 import com.ning.jetty.base.modules.ServerModuleBuilder;
 import com.ning.jetty.core.listeners.SetupServer;
 
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.PropertyNamingStrategy;
 import com.google.inject.Injector;
 import com.google.inject.Module;
 
-import org.codehaus.jackson.map.ObjectMapper;
-import org.codehaus.jackson.map.SerializationConfig;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -72,6 +72,9 @@ public class KillbillGuiceListener extends SetupServer
         killbillBusService = theInjector.getInstance(BusService.class);
         killbilleventHandler = theInjector.getInstance(KillbillEventHandler.class); 
         
+        ObjectMapper mapper = theInjector.getInstance(ObjectMapper.class);
+        mapper.setPropertyNamingStrategy(new PropertyNamingStrategy.LowerCaseWithUnderscoresStrategy());
+
         //
         // Fire all Startup levels up to service start
         //
diff --git a/server/src/main/java/com/ning/billing/server/modules/KillbillServerModule.java b/server/src/main/java/com/ning/billing/server/modules/KillbillServerModule.java
index 8a9e490..d6c3691 100644
--- a/server/src/main/java/com/ning/billing/server/modules/KillbillServerModule.java
+++ b/server/src/main/java/com/ning/billing/server/modules/KillbillServerModule.java
@@ -17,6 +17,7 @@
 package com.ning.billing.server.modules;
 
 import com.ning.billing.util.email.EmailModule;
+import com.ning.billing.util.email.templates.TemplateModule;
 import com.ning.billing.util.glue.GlobalLockerModule;
 import org.skife.jdbi.v2.DBI;
 import org.skife.jdbi.v2.IDBI;
@@ -26,16 +27,17 @@ import com.ning.billing.account.glue.AccountModule;
 import com.ning.billing.analytics.setup.AnalyticsModule;
 import com.ning.billing.beatrix.glue.BeatrixModule;
 import com.ning.billing.catalog.glue.CatalogModule;
-import com.ning.billing.entitlement.glue.EntitlementModule;
-import com.ning.billing.invoice.glue.InvoiceModule;
+import com.ning.billing.entitlement.glue.DefaultEntitlementModule;
+import com.ning.billing.invoice.glue.DefaultInvoiceModule;
 import com.ning.billing.jaxrs.resources.AccountResource;
 import com.ning.billing.jaxrs.resources.BundleResource;
-import com.ning.billing.jaxrs.resources.BundleTimelineResource;
 import com.ning.billing.jaxrs.resources.InvoiceResource;
 import com.ning.billing.jaxrs.resources.PaymentResource;
 import com.ning.billing.jaxrs.resources.SubscriptionResource;
+import com.ning.billing.jaxrs.resources.TagResource;
 import com.ning.billing.jaxrs.util.KillbillEventHandler;
-import com.ning.billing.junction.glue.JunctionModule;
+import com.ning.billing.jaxrs.util.TagHelper;
+import com.ning.billing.junction.glue.DefaultJunctionModule;
 import com.ning.billing.payment.setup.PaymentModule;
 import com.ning.billing.util.glue.BusModule;
 import com.ning.billing.util.glue.CallContextModule;
@@ -60,12 +62,13 @@ public class KillbillServerModule extends AbstractModule
     }
 
     protected void configureResources() {
+        bind(TagHelper.class).asEagerSingleton();
         bind(AccountResource.class).asEagerSingleton();
         bind(BundleResource.class).asEagerSingleton();
         bind(SubscriptionResource.class).asEagerSingleton();
-        bind(BundleTimelineResource.class).asEagerSingleton();
         bind(InvoiceResource.class).asEagerSingleton();
         bind(PaymentResource.class).asEagerSingleton();
+        bind(TagResource.class).asEagerSingleton();
         bind(KillbillEventHandler.class).asEagerSingleton();
     }
 
@@ -83,12 +86,13 @@ public class KillbillServerModule extends AbstractModule
         install(new NotificationQueueModule());
         install(new CallContextModule());
         install(new AccountModule());
-        install(new InvoiceModule());
-        install(new EntitlementModule());
+        install(new DefaultInvoiceModule());
+        install(new TemplateModule());
+        install(new DefaultEntitlementModule());
         install(new AnalyticsModule());
         install(new PaymentModule());
         install(new BeatrixModule());
-        install(new JunctionModule());        
+        install(new DefaultJunctionModule());        
         installClock();
     }
 }
diff --git a/server/src/test/java/com/ning/billing/jaxrs/TestAccount.java b/server/src/test/java/com/ning/billing/jaxrs/TestAccount.java
index a74d8df..4b21087 100644
--- a/server/src/test/java/com/ning/billing/jaxrs/TestAccount.java
+++ b/server/src/test/java/com/ning/billing/jaxrs/TestAccount.java
@@ -16,15 +16,20 @@
 package com.ning.billing.jaxrs;
 
 
+import static org.testng.Assert.assertEquals;
 import static org.testng.Assert.assertNotNull;
 
 import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
 import java.util.Map;
 
 
 import javax.ws.rs.core.Response.Status;
 
 
+import org.codehaus.jackson.map.ObjectMapper;
+import org.codehaus.jackson.map.PropertyNamingStrategy;
 import org.joda.time.DateTime;
 import org.joda.time.Interval;
 import org.slf4j.Logger;
@@ -38,7 +43,9 @@ import com.ning.billing.catalog.api.ProductCategory;
 import com.ning.billing.jaxrs.json.AccountJson;
 import com.ning.billing.jaxrs.json.AccountTimelineJson;
 import com.ning.billing.jaxrs.json.BundleJsonNoSubsciptions;
+import com.ning.billing.jaxrs.json.CustomFieldJson;
 import com.ning.billing.jaxrs.json.SubscriptionJsonNoEvents;
+import com.ning.billing.jaxrs.json.TagDefinitionJson;
 import com.ning.billing.jaxrs.resources.BaseJaxrsResource;
 import com.ning.http.client.Response;
 
@@ -48,6 +55,8 @@ public class TestAccount extends TestJaxrsBase {
 	private static final Logger log = LoggerFactory.getLogger(TestAccount.class);
 
 
+	
+	
 	@Test(groups="slow", enabled=true)
 	public void testAccountOk() throws Exception {
 		
@@ -138,6 +147,60 @@ public class TestAccount extends TestJaxrsBase {
         Assert.assertEquals(objFromJson.getBundles().size(), 1); 
         Assert.assertEquals(objFromJson.getBundles().get(0).getSubscriptions().size(), 1);
         Assert.assertEquals(objFromJson.getBundles().get(0).getSubscriptions().get(0).getEvents().size(), 2);        
- 
+ 	}
+
+	@Test(groups="slow", enabled=false)
+	public void testAccountWithTags() throws Exception {
+	    //Create Tag definition
+	    TagDefinitionJson input = new TagDefinitionJson("yoyo", "nothing more to say");
+	    String baseJson = mapper.writeValueAsString(input);
+	    Response response = doPost(BaseJaxrsResource.TAG_DEFINITIONS_PATH, baseJson, DEFAULT_EMPTY_QUERY, DEFAULT_HTTP_TIMEOUT_SEC);
+	    assertEquals(response.getStatusCode(), Status.CREATED.getStatusCode());
+	    
+	    AccountJson accountJson = createAccount("couroucoucou", "shdwdsqgfhwe", "couroucoucou@yahoo.com");
+	    assertNotNull(accountJson);
+	        
+	    Map<String, String> queryParams = new HashMap<String, String>();
+        queryParams.put(BaseJaxrsResource.QUERY_TAGS, input.getName());
+        String uri = BaseJaxrsResource.ACCOUNTS_PATH + "/" + BaseJaxrsResource.TAGS + "/" + accountJson.getAcountId() ;
+	    response = doPost(uri, null, queryParams, DEFAULT_HTTP_TIMEOUT_SEC);
+        assertEquals(response.getStatusCode(), Status.CREATED.getStatusCode());
+        
+        /*
+         * STEPH Some how Location returns the ID twice (confused) :
+         * Location: http://127.0.0.1:8080/1.0/kb/accounts/tags/ebb5f830-6f0a-4521-9553-521d173169be/ebb5f830-6f0a-4521-9553-521d173169be
+         * 
+        String location = response.getHeader("Location");
+        Assert.assertNotNull(location);
+
+        // Retrieves by Id based on Location returned
+        response = doGetWithUrl(location, DEFAULT_EMPTY_QUERY, DEFAULT_HTTP_TIMEOUT_SEC);
+        Assert.assertEquals(response.getStatusCode(), Status.OK.getStatusCode());
+        */
+
+	}
+	
+    @Test(groups="slow", enabled=false)
+	public void testAccountWithCustomFields() throws Exception {
+        
+        AccountJson accountJson = createAccount("carafe", "shdwhwgaz", "carafe@yahoo.com");
+        assertNotNull(accountJson);
+        
+        List<CustomFieldJson> customFields =  new LinkedList<CustomFieldJson>();
+        customFields.add(new CustomFieldJson("1", "value1"));
+        customFields.add(new CustomFieldJson("2", "value2"));
+        customFields.add(new CustomFieldJson("3", "value3"));  
+        String baseJson = mapper.writeValueAsString(customFields);
+
+        String uri = BaseJaxrsResource.ACCOUNTS_PATH + "/" + BaseJaxrsResource.CUSTOM_FIELDS + "/" + accountJson.getAcountId() ;
+        Response response = doPost(uri,baseJson, DEFAULT_EMPTY_QUERY, DEFAULT_HTTP_TIMEOUT_SEC);
+        assertEquals(response.getStatusCode(), Status.CREATED.getStatusCode());
+        String location = response.getHeader("Location");
+        Assert.assertNotNull(location);
+
+        // Retrieves by Id based on Location returned
+        response = doGetWithUrl(location, DEFAULT_EMPTY_QUERY, DEFAULT_HTTP_TIMEOUT_SEC);
+        Assert.assertEquals(response.getStatusCode(), Status.OK.getStatusCode());
+        
 	}
 }
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 0d1f1cd..f0182bd 100644
--- a/server/src/test/java/com/ning/billing/jaxrs/TestBundle.java
+++ b/server/src/test/java/com/ning/billing/jaxrs/TestBundle.java
@@ -23,12 +23,12 @@ import java.util.Map;
 
 import javax.ws.rs.core.Response.Status;
 
-import org.codehaus.jackson.type.TypeReference;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.testng.Assert;
 import org.testng.annotations.Test;
 
+import com.fasterxml.jackson.core.type.TypeReference;
 import com.ning.billing.jaxrs.json.AccountJson;
 import com.ning.billing.jaxrs.json.BundleJsonNoSubsciptions;
 import com.ning.billing.jaxrs.resources.BaseJaxrsResource;
diff --git a/server/src/test/java/com/ning/billing/jaxrs/TestInvoice.java b/server/src/test/java/com/ning/billing/jaxrs/TestInvoice.java
new file mode 100644
index 0000000..9a039fd
--- /dev/null
+++ b/server/src/test/java/com/ning/billing/jaxrs/TestInvoice.java
@@ -0,0 +1,126 @@
+/* 
+ * 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.jaxrs;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNotNull;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.ws.rs.core.Response.Status;
+
+import org.joda.time.DateTime;
+import org.joda.time.Interval;
+import org.joda.time.format.DateTimeFormatter;
+import org.joda.time.format.ISODateTimeFormat;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.ning.billing.catalog.api.BillingPeriod;
+import com.ning.billing.catalog.api.ProductCategory;
+import com.ning.billing.jaxrs.json.AccountJson;
+import com.ning.billing.jaxrs.json.BundleJsonNoSubsciptions;
+import com.ning.billing.jaxrs.json.InvoiceJson;
+import com.ning.billing.jaxrs.json.SubscriptionJsonNoEvents;
+import com.ning.billing.jaxrs.resources.BaseJaxrsResource;
+import com.ning.http.client.Response;
+
+public class TestInvoice extends TestJaxrsBase  {
+
+    private final DateTimeFormatter DATE_TIME_FORMATTER = ISODateTimeFormat.dateTime();
+    
+    private static final Logger log = LoggerFactory.getLogger(TestInvoice.class);
+
+
+    @Test(groups="slow", enabled=true)
+    public void testInvoiceOk() throws Exception {
+        
+        DateTime initialDate = new DateTime(2012, 4, 25, 0, 3, 42, 0);
+        clock.setDeltaFromReality(initialDate.getMillis() - clock.getUTCNow().getMillis());
+        
+        
+        AccountJson accountJson = createAccount("poupou", "qhddffrwe", "poupou@yahoo.com");
+        assertNotNull(accountJson);
+        
+        BundleJsonNoSubsciptions bundleJson = createBundle(accountJson.getAcountId(), "9967599");
+        assertNotNull(bundleJson);
+        
+        SubscriptionJsonNoEvents subscriptionJson = createSubscription(bundleJson.getBundleId(), "Shotgun", ProductCategory.BASE.toString(), BillingPeriod.MONTHLY.toString(), true);
+        assertNotNull(subscriptionJson);
+        
+        // MOVE AFTER TRIAL
+        Interval it = new Interval(clock.getUTCNow(), clock.getUTCNow().plusMonths(3).plusDays(1));
+        clock.addDeltaFromReality(it.toDurationMillis());
+
+        crappyWaitForLackOfProperSynchonization();
+        
+        String uri = BaseJaxrsResource.INVOICES_PATH;
+        Map<String, String> queryParams = new HashMap<String, String>();
+        queryParams.put(BaseJaxrsResource.QUERY_ACCOUNT_ID, accountJson.getAcountId());
+        
+        Response response = doGet(uri, queryParams, DEFAULT_HTTP_TIMEOUT_SEC);
+        Assert.assertEquals(response.getStatusCode(), Status.OK.getStatusCode());
+        String baseJson = response.getResponseBody();
+        List<InvoiceJson> objFromJson = mapper.readValue(baseJson, new TypeReference<List<InvoiceJson>>() {});
+        assertNotNull(objFromJson);
+        log.info(baseJson);
+        assertEquals(objFromJson.size(), 4);
+        
+        // Check we can retrieve an individual invoice
+        uri = BaseJaxrsResource.INVOICES_PATH + "/" + objFromJson.get(0).getInvoiceId();
+        response = doGet(uri, DEFAULT_EMPTY_QUERY, DEFAULT_HTTP_TIMEOUT_SEC);
+        Assert.assertEquals(response.getStatusCode(), Status.OK.getStatusCode());        
+        baseJson = response.getResponseBody();
+        InvoiceJson firstInvoiceJson = mapper.readValue(baseJson, InvoiceJson.class);
+        assertNotNull(objFromJson);    
+        assertEquals(firstInvoiceJson, objFromJson.get(0));
+        
+        // Then create a dryRun Invoice
+        DateTime futureDate = clock.getUTCNow().plusMonths(1).plusDays(3);
+        uri = BaseJaxrsResource.INVOICES_PATH;
+        queryParams.put(BaseJaxrsResource.QUERY_TARGET_DATE, futureDate.toString());
+        queryParams.put(BaseJaxrsResource.QUERY_DRY_RUN, "true");        
+        response = doPost(uri, null, queryParams, DEFAULT_HTTP_TIMEOUT_SEC);
+        Assert.assertEquals(response.getStatusCode(), Status.OK.getStatusCode()); 
+        baseJson = response.getResponseBody();
+        InvoiceJson futureInvoice = mapper.readValue(baseJson, InvoiceJson.class);
+        assertNotNull(futureInvoice);    
+        log.info(baseJson);
+        
+        // The one more time with no DryRun
+        queryParams.remove(BaseJaxrsResource.QUERY_DRY_RUN);
+        response = doPost(uri, null, queryParams, DEFAULT_HTTP_TIMEOUT_SEC);
+        Assert.assertEquals(response.getStatusCode(), Status.CREATED.getStatusCode());
+        
+        String location = response.getHeader("Location");
+        Assert.assertNotNull(location);
+        
+        // Check again # invoices, should be 5 this time
+        uri = BaseJaxrsResource.INVOICES_PATH;
+        response = doGet(uri, queryParams, DEFAULT_HTTP_TIMEOUT_SEC);
+        assertEquals(response.getStatusCode(), Status.OK.getStatusCode());
+        baseJson = response.getResponseBody();
+        objFromJson = mapper.readValue(baseJson, new TypeReference<List<InvoiceJson>>() {});
+        assertNotNull(objFromJson);
+        log.info(baseJson);
+        assertEquals(objFromJson.size(), 5);
+    }
+}
diff --git a/server/src/test/java/com/ning/billing/jaxrs/TestJaxrsBase.java b/server/src/test/java/com/ning/billing/jaxrs/TestJaxrsBase.java
index e3c9db4..cf9eddd 100644
--- a/server/src/test/java/com/ning/billing/jaxrs/TestJaxrsBase.java
+++ b/server/src/test/java/com/ning/billing/jaxrs/TestJaxrsBase.java
@@ -31,9 +31,9 @@ import java.util.concurrent.TimeUnit;
 import javax.ws.rs.core.Response.Status;
 
 import com.ning.billing.util.email.EmailModule;
+import com.ning.billing.util.email.templates.TemplateModule;
 import com.ning.billing.util.glue.GlobalLockerModule;
 import org.apache.commons.io.IOUtils;
-import org.codehaus.jackson.map.ObjectMapper;
 import org.eclipse.jetty.servlet.FilterHolder;
 import org.skife.config.ConfigurationObjectFactory;
 import org.skife.jdbi.v2.IDBI;
@@ -45,11 +45,15 @@ import org.testng.annotations.BeforeClass;
 import org.testng.annotations.BeforeMethod;
 import org.testng.annotations.BeforeSuite;
 
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.PropertyNamingStrategy;
+import com.fasterxml.jackson.databind.SerializationFeature;
+import com.fasterxml.jackson.datatype.joda.JodaModule;
 import com.google.inject.Module;
 import com.ning.billing.account.glue.AccountModule;
 import com.ning.billing.analytics.setup.AnalyticsModule;
+import com.ning.billing.api.TestApiListener;
 import com.ning.billing.beatrix.glue.BeatrixModule;
-import com.ning.billing.beatrix.integration.TestBusHandler;
 import com.ning.billing.beatrix.integration.TestIntegration;
 import com.ning.billing.catalog.api.PriceListSet;
 import com.ning.billing.catalog.glue.CatalogModule;
@@ -57,13 +61,13 @@ import com.ning.billing.config.PaymentConfig;
 import com.ning.billing.dbi.DBIProvider;
 import com.ning.billing.dbi.DbiConfig;
 import com.ning.billing.dbi.MysqlTestingHelper;
-import com.ning.billing.entitlement.glue.EntitlementModule;
-import com.ning.billing.invoice.glue.InvoiceModule;
+import com.ning.billing.entitlement.glue.DefaultEntitlementModule;
+import com.ning.billing.invoice.glue.DefaultInvoiceModule;
 import com.ning.billing.jaxrs.json.AccountJson;
 import com.ning.billing.jaxrs.json.BundleJsonNoSubsciptions;
 import com.ning.billing.jaxrs.json.SubscriptionJsonNoEvents;
 import com.ning.billing.jaxrs.resources.BaseJaxrsResource;
-import com.ning.billing.junction.glue.JunctionModule;
+import com.ning.billing.junction.glue.DefaultJunctionModule;
 import com.ning.billing.payment.provider.MockPaymentProviderPluginModule;
 import com.ning.billing.payment.setup.PaymentModule;
 import com.ning.billing.server.listeners.KillbillGuiceListener;
@@ -87,7 +91,7 @@ public class TestJaxrsBase {
 
     private final static String PLUGIN_NAME = "noop";
 
-    protected static final int DEFAULT_HTTP_TIMEOUT_SEC =  5;
+    protected static final int DEFAULT_HTTP_TIMEOUT_SEC = 5;
 
     protected static final Map<String, String> DEFAULT_EMPTY_QUERY = new HashMap<String, String>();
 
@@ -106,8 +110,13 @@ public class TestJaxrsBase {
     protected AsyncHttpClient httpClient;	
     protected ObjectMapper mapper;
     protected ClockMock clock;
-    protected TestBusHandler busHandler;
+    protected TestApiListener busHandler;
 
+    // Context informtation to be passed around
+    private static final String createdBy = "Toto";
+    private static final String reason = "i am god";
+    private static final String comment = "no comment";    
+    
     public static void loadSystemPropertiesFromClasspath(final String resource) {
         final URL url = TestJaxrsBase.class.getResource(resource);
         assertNotNull(url);
@@ -188,12 +197,13 @@ public class TestJaxrsBase {
             install(new NotificationQueueModule());
             install(new CallContextModule());
             install(new AccountModule());
-            install(new InvoiceModule());
-            install(new EntitlementModule());
+            install(new DefaultInvoiceModule());
+            install(new TemplateModule());
+            install(new DefaultEntitlementModule());
             install(new AnalyticsModule());
             install(new PaymentMockModule());
             install(new BeatrixModule());
-            install(new JunctionModule());
+            install(new DefaultJunctionModule());
             installClock();
         }
 
@@ -222,7 +232,12 @@ public class TestJaxrsBase {
         loadConfig();
         httpClient = new AsyncHttpClient();
         mapper = new ObjectMapper();
-        busHandler = new TestBusHandler(null);
+        mapper.registerModule(new JodaModule());
+        mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
+
+        mapper.setPropertyNamingStrategy(new PropertyNamingStrategy.LowerCaseWithUnderscoresStrategy());        
+
+        busHandler = new TestApiListener(null);
         this.helper = listener.getMysqlTestingHelper();
         this.clock =  (ClockMock) listener.getClock();
     }
@@ -372,8 +387,10 @@ public class TestJaxrsBase {
         BoundRequestBuilder builder = getBuilderWithHeaderAndQuery("POST", getUrlFromUri(uri), queryParams);
         if (body != null) {
             builder.setBody(body);
+        } else {
+            builder.setBody("{}");
         }
-        return executeAndWait(builder, timeoutSec);
+        return executeAndWait(builder, timeoutSec, true);
     }
 
     protected Response doPut(final String uri, final String body, final Map<String, String> queryParams, final int timeoutSec) {
@@ -381,14 +398,16 @@ public class TestJaxrsBase {
         BoundRequestBuilder builder = getBuilderWithHeaderAndQuery("PUT", url, queryParams);
         if (body != null) {
             builder.setBody(body);
+        } else {
+            builder.setBody("{}");
         }
-        return executeAndWait(builder, timeoutSec);
+        return executeAndWait(builder, timeoutSec, true);
     }
 
     protected Response doDelete(final String uri, final Map<String, String> queryParams, final int timeoutSec) {
         final String url = String.format("http://%s:%d%s", config.getServerHost(), config.getServerPort(), uri);
         BoundRequestBuilder builder = getBuilderWithHeaderAndQuery("DELETE", url, queryParams);
-        return executeAndWait(builder, timeoutSec);
+        return executeAndWait(builder, timeoutSec, true);
     }
 
     protected Response doGet(final String uri, final Map<String, String> queryParams, final int timeoutSec) {
@@ -398,10 +417,17 @@ public class TestJaxrsBase {
 
     protected Response doGetWithUrl(final String url, final Map<String, String> queryParams, final int timeoutSec) {
         BoundRequestBuilder builder = getBuilderWithHeaderAndQuery("GET", url, queryParams);
-        return executeAndWait(builder, timeoutSec);
+        return executeAndWait(builder, timeoutSec, false);
     }
 
-    private Response executeAndWait(final BoundRequestBuilder builder, final int timeoutSec) {
+    private Response executeAndWait(final BoundRequestBuilder builder, final int timeoutSec, final boolean addContextHeader) {
+        
+        if (addContextHeader) {
+            builder.addHeader(BaseJaxrsResource.HDR_CREATED_BY, createdBy);
+            builder.addHeader(BaseJaxrsResource.HDR_REASON, reason);
+            builder.addHeader(BaseJaxrsResource.HDR_COMMENT, comment);            
+        }
+        
         Response response = null;
         try {
             ListenableFuture<Response> futureStatus = 
@@ -465,7 +491,7 @@ public class TestJaxrsBase {
      * but until we have a strong need for it, this is in the TODO list...
      */
     protected void crappyWaitForLackOfProperSynchonization() throws Exception {
-        Thread.sleep(5000);
+        Thread.sleep(7000);
     }
 
 }
diff --git a/server/src/test/java/com/ning/billing/jaxrs/TestTag.java b/server/src/test/java/com/ning/billing/jaxrs/TestTag.java
new file mode 100644
index 0000000..c1f460a
--- /dev/null
+++ b/server/src/test/java/com/ning/billing/jaxrs/TestTag.java
@@ -0,0 +1,114 @@
+/* 
+ * 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.jaxrs;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNotNull;
+
+import java.util.List;
+
+import javax.ws.rs.core.Response.Status;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.testng.annotations.Test;
+
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.ning.billing.jaxrs.json.TagDefinitionJson;
+import com.ning.billing.jaxrs.resources.BaseJaxrsResource;
+import com.ning.http.client.Response;
+
+public class TestTag extends TestJaxrsBase {
+
+    private static final Logger log = LoggerFactory.getLogger(TestTag.class);
+
+    @Test(groups="slow", enabled=true)
+    public void testTagDefinitionOk() throws Exception {
+    
+        TagDefinitionJson input = new TagDefinitionJson("blue", "realxing color");
+        String baseJson = mapper.writeValueAsString(input);
+        Response response = doPost(BaseJaxrsResource.TAG_DEFINITIONS_PATH, baseJson, DEFAULT_EMPTY_QUERY, DEFAULT_HTTP_TIMEOUT_SEC);
+        assertEquals(response.getStatusCode(), Status.CREATED.getStatusCode());
+
+        String location = response.getHeader("Location");
+        assertNotNull(location);
+
+        // Retrieves by Id based on Location returned
+        response = doGetWithUrl(location, DEFAULT_EMPTY_QUERY, DEFAULT_HTTP_TIMEOUT_SEC);
+        assertEquals(response.getStatusCode(), Status.OK.getStatusCode());
+
+        baseJson = response.getResponseBody();
+        TagDefinitionJson objFromJson = mapper.readValue(baseJson, TagDefinitionJson.class);
+        assertNotNull(objFromJson);
+        assertEquals(objFromJson, input);
+    }
+    
+    @Test(groups="slow", enabled=true)
+    public void testMultipleTagDefinitionOk() throws Exception {
+    
+        Response response = doGet(BaseJaxrsResource.TAG_DEFINITIONS_PATH, DEFAULT_EMPTY_QUERY, DEFAULT_HTTP_TIMEOUT_SEC);
+        assertEquals(response.getStatusCode(), Status.OK.getStatusCode());
+        String baseJson = response.getResponseBody();
+        
+        List<TagDefinitionJson> objFromJson = mapper.readValue(baseJson, new TypeReference<List<TagDefinitionJson>>() {});
+        int sizeSystemTag = (objFromJson == null || objFromJson.size() == 0) ? 0 : objFromJson.size();
+        
+        TagDefinitionJson input = new TagDefinitionJson("blue", "realxing color");
+        baseJson = mapper.writeValueAsString(input);
+        response = doPost(BaseJaxrsResource.TAG_DEFINITIONS_PATH, baseJson, DEFAULT_EMPTY_QUERY, DEFAULT_HTTP_TIMEOUT_SEC);
+        assertEquals(response.getStatusCode(), Status.CREATED.getStatusCode());
+
+        input = new TagDefinitionJson("red", "hot color");
+        baseJson = mapper.writeValueAsString(input);
+        response = doPost(BaseJaxrsResource.TAG_DEFINITIONS_PATH, baseJson, DEFAULT_EMPTY_QUERY, DEFAULT_HTTP_TIMEOUT_SEC);
+        assertEquals(response.getStatusCode(), Status.CREATED.getStatusCode());
+
+        input = new TagDefinitionJson("yellow", "vibrant color");
+        baseJson = mapper.writeValueAsString(input);
+        response = doPost(BaseJaxrsResource.TAG_DEFINITIONS_PATH, baseJson, DEFAULT_EMPTY_QUERY, DEFAULT_HTTP_TIMEOUT_SEC);
+        assertEquals(response.getStatusCode(), Status.CREATED.getStatusCode());
+
+        input = new TagDefinitionJson("green", "super realxing color");
+        baseJson = mapper.writeValueAsString(input);
+        response = doPost(BaseJaxrsResource.TAG_DEFINITIONS_PATH, baseJson, DEFAULT_EMPTY_QUERY, DEFAULT_HTTP_TIMEOUT_SEC);
+        assertEquals(response.getStatusCode(), Status.CREATED.getStatusCode());
+
+        response = doGet(BaseJaxrsResource.TAG_DEFINITIONS_PATH, DEFAULT_EMPTY_QUERY, DEFAULT_HTTP_TIMEOUT_SEC);
+        assertEquals(response.getStatusCode(), Status.OK.getStatusCode());
+        baseJson = response.getResponseBody();
+        
+        objFromJson = mapper.readValue(baseJson, new TypeReference<List<TagDefinitionJson>>() {});
+        assertNotNull(objFromJson);
+        assertEquals(objFromJson.size(), 4 + sizeSystemTag);
+
+        // STEPH currently broken Tag API does not work as expected...
+        
+        /*
+        String uri = BaseJaxrsResource.TAG_DEFINITIONS_PATH + "/green"; 
+        response = doDelete(uri, DEFAULT_EMPTY_QUERY, DEFAULT_HTTP_TIMEOUT_SEC);
+        assertEquals(response.getStatusCode(), Status.NO_CONTENT.getStatusCode());
+    
+        response = doGet(BaseJaxrsResource.TAG_DEFINITIONS_PATH, DEFAULT_EMPTY_QUERY, DEFAULT_HTTP_TIMEOUT_SEC);
+        assertEquals(response.getStatusCode(), Status.OK.getStatusCode());
+        baseJson = response.getResponseBody();
+        
+        objFromJson = mapper.readValue(baseJson, new TypeReference<List<TagDefinitionJson>>() {});
+        assertNotNull(objFromJson);
+        assertEquals(objFromJson.size(), 3 + sizeSystemTag);
+        */
+    }
+    
+}
diff --git a/util/src/main/java/com/ning/billing/util/bus/dao/BusEventEntry.java b/util/src/main/java/com/ning/billing/util/bus/dao/BusEventEntry.java
index 94f5a8d..b9c6faa 100644
--- a/util/src/main/java/com/ning/billing/util/bus/dao/BusEventEntry.java
+++ b/util/src/main/java/com/ning/billing/util/bus/dao/BusEventEntry.java
@@ -23,13 +23,15 @@ public class BusEventEntry implements NotificationLifecycle  {
     
     private final long id;
     private final String owner;
+    private final String createdOwner;
     private final DateTime nextAvailable;
     private final NotificationLifecycleState processingState;
     private final String busEventClass;
     private final String busEventJson;
 
-    public BusEventEntry(final long id, final String owner, final DateTime nextAvailable, NotificationLifecycleState processingState, final String busEventClass, final String busEventJson) {
+    public BusEventEntry(final long id, final String createdOwner, final String owner, final DateTime nextAvailable, NotificationLifecycleState processingState, final String busEventClass, final String busEventJson) {
         this.id = id;
+        this.createdOwner = createdOwner;
         this.owner = owner;
         this.nextAvailable = nextAvailable;
         this.processingState = processingState;
@@ -37,8 +39,8 @@ public class BusEventEntry implements NotificationLifecycle  {
         this.busEventJson = busEventJson;
     }
 
-    public BusEventEntry(final String busEventClass, final String busEventJson) {
-        this(0, null, null, null, busEventClass, busEventJson);
+    public BusEventEntry(final String createdOwner, final String busEventClass, final String busEventJson) {
+        this(0, createdOwner, null, null, null, busEventClass, busEventJson);
     }
 
     
@@ -61,6 +63,11 @@ public class BusEventEntry implements NotificationLifecycle  {
     }
 
     @Override
+    public String getCreatedOwner() {
+        return createdOwner;
+    }
+
+    @Override
     public DateTime getNextAvailableDate() {
         return nextAvailable;
     }
diff --git a/util/src/main/java/com/ning/billing/util/bus/dao/PersistentBusSqlDao.java b/util/src/main/java/com/ning/billing/util/bus/dao/PersistentBusSqlDao.java
index 406a2f8..7da7d08 100644
--- a/util/src/main/java/com/ning/billing/util/bus/dao/PersistentBusSqlDao.java
+++ b/util/src/main/java/com/ning/billing/util/bus/dao/PersistentBusSqlDao.java
@@ -42,7 +42,7 @@ public interface PersistentBusSqlDao extends Transactional<PersistentBusSqlDao>,
     
     @SqlQuery
     @Mapper(PersistentBusSqlMapper.class)
-    public BusEventEntry getNextBusEventEntry(@Bind("max") int max, @Bind("now") Date now);
+    public BusEventEntry getNextBusEventEntry(@Bind("max") int max, @Bind("owner") String owner, @Bind("now") Date now);
     
     @SqlUpdate
     public int claimBusEvent(@Bind("owner") String owner, @Bind("nextAvailable") Date nextAvailable,
@@ -69,6 +69,7 @@ public interface PersistentBusSqlDao extends Transactional<PersistentBusSqlDao>,
             stmt.bind("className", evt.getBusEventClass());
             stmt.bind("eventJson", evt.getBusEventJson());
             stmt.bind("createdDate", getDate(new DateTime()));
+            stmt.bind("creatingOwner", evt.getCreatedOwner());
             stmt.bind("processingAvailableDate", getDate(evt.getNextAvailableDate()));
             stmt.bind("processingOwner", evt.getOwner());
             stmt.bind("processingState", NotificationLifecycleState.AVAILABLE.toString());
@@ -83,12 +84,13 @@ public interface PersistentBusSqlDao extends Transactional<PersistentBusSqlDao>,
 
             final Long recordId = r.getLong("record_id");
             final String className = r.getString("class_name"); 
+            final String createdOwner = r.getString("creating_owner");
             final String eventJson = r.getString("event_json"); 
             final DateTime nextAvailableDate = getDate(r, "processing_available_date");
             final String processingOwner = r.getString("processing_owner");
             final NotificationLifecycleState processingState = NotificationLifecycleState.valueOf(r.getString("processing_state"));
             
-            return new BusEventEntry(recordId, processingOwner, nextAvailableDate, processingState, className, eventJson);
+            return new BusEventEntry(recordId, createdOwner, processingOwner, nextAvailableDate, processingState, className, eventJson);
         }
     }
 }
diff --git a/util/src/main/java/com/ning/billing/util/bus/PersistentBus.java b/util/src/main/java/com/ning/billing/util/bus/PersistentBus.java
index 004e3ab..bfce245 100644
--- a/util/src/main/java/com/ning/billing/util/bus/PersistentBus.java
+++ b/util/src/main/java/com/ning/billing/util/bus/PersistentBus.java
@@ -19,11 +19,8 @@ package com.ning.billing.util.bus;
 import java.util.Collections;
 import java.util.Date;
 import java.util.List;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
 import java.util.concurrent.ThreadFactory;
-import java.util.concurrent.TimeUnit;
 
 import org.codehaus.jackson.map.ObjectMapper;
 import org.codehaus.jackson.map.SerializationConfig;
@@ -40,11 +37,12 @@ import com.ning.billing.util.Hostname;
 import com.ning.billing.util.bus.dao.BusEventEntry;
 import com.ning.billing.util.bus.dao.PersistentBusSqlDao;
 import com.ning.billing.util.clock.Clock;
+import com.ning.billing.util.queue.PersistentQueueBase;
 
 
-public class PersistentBus implements Bus  {
+public class PersistentBus extends PersistentQueueBase implements Bus {
 
-    private final static int NB_BUS_THREADS = 3;
+    private final static int NB_BUS_THREADS = 1;
     private final static long TIMEOUT_MSEC = 15L * 1000L; // 15 sec
     private final static long DELTA_IN_PROCESSING_TIME_MS = 1000L * 60L * 5L; // 5 minutes
     private final static long SLEEP_TIME_MS = 1000; // 1 sec
@@ -53,16 +51,13 @@ public class PersistentBus implements Bus  {
     private static final Logger log = LoggerFactory.getLogger(PersistentBus.class);
     
     private final PersistentBusSqlDao dao;
-    private final ExecutorService executor;
     
     private final ObjectMapper objectMapper;
     private final EventBusDelegate eventBusDelegate;
     private final Clock clock;
     private final String hostname;
     
-    protected boolean isProcessingEvents;
-    private int curActiveThreads;
-    
+
     
     private static final class EventBusDelegate extends EventBus {
         public EventBusDelegate(String busName) {
@@ -86,110 +81,34 @@ public class PersistentBus implements Bus  {
     
     @Inject
     public PersistentBus(final IDBI dbi, final Clock clock) {
+        super("Bus", Executors.newFixedThreadPool(NB_BUS_THREADS, new ThreadFactory() {
+            @Override
+            public Thread newThread(Runnable r) {
+                return new Thread(new ThreadGroup(DefaultBusService.EVENT_BUS_GROUP_NAME),
+                        r,
+                        DefaultBusService.EVENT_BUS_TH_NAME);
+            }
+        }), NB_BUS_THREADS, TIMEOUT_MSEC, SLEEP_TIME_MS);
         this.dao = dbi.onDemand(PersistentBusSqlDao.class);
         this.clock = clock;
         this.objectMapper = new ObjectMapper();
         this.objectMapper.disable(SerializationConfig.Feature.WRITE_DATES_AS_TIMESTAMPS);
         this.eventBusDelegate = new EventBusDelegate("Killbill EventBus");
-        final ThreadGroup group = new ThreadGroup(DefaultBusService.EVENT_BUS_GROUP_NAME);
-        this.executor = Executors.newFixedThreadPool(NB_BUS_THREADS, new ThreadFactory() {
-            @Override
-            public Thread newThread(Runnable r) {
-                return new Thread(group, r, DefaultBusService.EVENT_BUS_TH_NAME);
-            }
-        });
         this.hostname = Hostname.get();
-        this.isProcessingEvents = false;
     }
-
-    
     
     @Override
     public void start() {
-        
-        isProcessingEvents = true;
-        curActiveThreads = 0;
-        
-        final PersistentBus thePersistentBus = this;
-        
-        final CountDownLatch doneInitialization = new CountDownLatch(NB_BUS_THREADS);
-        
-        for (int i = 0; i < NB_BUS_THREADS; i++) {
-            executor.execute(new Runnable() {
-                @Override
-                public void run() {
-
-                    log.info(String.format("PersistentBus thread %s [%d] started",
-                            Thread.currentThread().getName(),
-                            Thread.currentThread().getId()));
-                    
-                    synchronized(thePersistentBus) {
-                        curActiveThreads++;
-                    }
-                    
-                    doneInitialization.countDown();
-                    
-                    try {
-                        while (true) {
-                            
-                            synchronized(thePersistentBus) {
-                                if (!isProcessingEvents) {
-                                    thePersistentBus.notify();
-                                    break;
-                                }
-                            }
-
-                            try {
-                                doProcessEvents();
-                            } catch (Exception e) {
-                                log.error(String.format("PersistentBus thread  %s  [%d] got an exception..",
-                                        Thread.currentThread().getName(),
-                                        Thread.currentThread().getId()), e);
-                            }
-                            sleepALittle();
-                        }
-                    } catch (InterruptedException e) {
-                        log.info(Thread.currentThread().getName() + " got interrupted, exting...");
-                    } catch (Throwable e) {
-                        log.error(Thread.currentThread().getName() + " got an exception exiting...", e);
-                        // Just to make it really obvious in the log
-                        e.printStackTrace();
-                    } finally {
-                        
-                        log.info(String.format("PersistentBus thread %s [%d] exited",
-                                Thread.currentThread().getName(),
-                                Thread.currentThread().getId()));
-                    
-                        synchronized(thePersistentBus) {
-                            curActiveThreads--;
-                        }
-                    }
-                }
-                
-                private void sleepALittle() throws InterruptedException {
-                    Thread.sleep(SLEEP_TIME_MS);
-                }
-            });
-        }
-        try {
-            doneInitialization.await(TIMEOUT_MSEC, TimeUnit.SECONDS);
-        } catch (InterruptedException e) {
-            log.warn("PersistentBus start sequence got interrupted...");
-        }
+        startQueue();
     }
-    
-    
-    private BusEvent deserializeBusEvent(final String className, final String json) {
-        try {
-            Class<?> claz = Class.forName(className);
-            return (BusEvent) objectMapper.readValue(json, claz);
-        } catch (Exception e) {
-            log.error(String.format("Failed to deserialize json object %s for class %s", json, className), e);
-            return null;
-        }
+
+    @Override
+    public void stop() {
+        stopQueue();
     }
-    
-    private int doProcessEvents() {
+
+    @Override
+    public int doProcessEvents() {
 
         List<BusEventEntry> events = getNextBusEvent();
         if (events.size() == 0) {
@@ -198,22 +117,32 @@ public class PersistentBus implements Bus  {
 
         int result = 0;
         for (final BusEventEntry cur : events) {
-            BusEvent e = deserializeBusEvent(cur.getBusEventClass(), cur.getBusEventJson());
+            BusEvent evt = deserializeBusEvent(cur.getBusEventClass(), cur.getBusEventJson());
             result++;
-            // STEPH need to look at failure cases
-            eventBusDelegate.post(e);
+            // STEPH exception handling is done by GUAVA-- logged a bug Issue-780
+            eventBusDelegate.post(evt);
             dao.clearBusEvent(cur.getId(), hostname);
         }
         return result;
     }
 
+    private BusEvent deserializeBusEvent(final String className, final String json) {
+        try {
+            Class<?> claz = Class.forName(className);
+            return (BusEvent) objectMapper.readValue(json, claz);
+        } catch (Exception e) {
+            log.error(String.format("Failed to deserialize json object %s for class %s", json, className), e);
+            return null;
+        }
+    }
+
     
     private List<BusEventEntry> getNextBusEvent() {
 
         final Date now = clock.getUTCNow().toDate();
         final Date nextAvailable = clock.getUTCNow().plus(DELTA_IN_PROCESSING_TIME_MS).toDate();
 
-        BusEventEntry input = dao.getNextBusEventEntry(MAX_BUS_EVENTS, now);
+        BusEventEntry input = dao.getNextBusEventEntry(MAX_BUS_EVENTS, hostname, now);
         if (input == null) {
             return Collections.emptyList();
         }
@@ -226,34 +155,6 @@ public class PersistentBus implements Bus  {
         return Collections.emptyList();
     }
 
-
-    @Override
-    public void stop() {
-        int remaining = 0;
-        try {
-            synchronized(this) {
-                isProcessingEvents = false;
-                long ini = System.currentTimeMillis();
-                long remainingWaitTimeMs = TIMEOUT_MSEC;
-                while (curActiveThreads > 0 && remainingWaitTimeMs > 0) {
-                    wait(1000);
-                    remainingWaitTimeMs = TIMEOUT_MSEC - (System.currentTimeMillis() - ini);
-                }
-                remaining = curActiveThreads;
-            }
-            
-        } catch (InterruptedException ignore) {
-            log.info("PersistentBus has been interrupted during stop sequence");
-        } finally {
-            if (remaining > 0) {
-                log.error(String.format("PersistentBus stopped with %d active remaing threads", remaining));
-            } else {
-                log.info("PersistentBus completed sucesfully shutdown sequence");
-            }
-            curActiveThreads = 0;
-        }
-    }
-
     @Override
     public void register(Object handlerInstance) throws EventBusException {
         eventBusDelegate.register(handlerInstance);
@@ -286,7 +187,7 @@ public class PersistentBus implements Bus  {
     private void postFromTransaction(BusEvent event, PersistentBusSqlDao transactional) {
         try {
             String json = objectMapper.writeValueAsString(event);
-            BusEventEntry entry  =  new BusEventEntry(event.getClass().getName(), json);
+            BusEventEntry entry  =  new BusEventEntry(hostname, event.getClass().getName(), json);
             transactional.insertBusEvent(entry);
         } catch (Exception e) {
             log.error("Failed to post BusEvent " + event.toString(), e);
diff --git a/util/src/main/java/com/ning/billing/util/customfield/dao/AuditedCustomFieldDao.java b/util/src/main/java/com/ning/billing/util/customfield/dao/AuditedCustomFieldDao.java
index accb6b8..b918fac 100644
--- a/util/src/main/java/com/ning/billing/util/customfield/dao/AuditedCustomFieldDao.java
+++ b/util/src/main/java/com/ning/billing/util/customfield/dao/AuditedCustomFieldDao.java
@@ -28,7 +28,7 @@ public class AuditedCustomFieldDao extends AuditedCollectionDaoBase<CustomField>
     private final CustomFieldSqlDao dao;
 
     @Inject
-    public AuditedCustomFieldDao(IDBI dbi) {
+    public AuditedCustomFieldDao(final IDBI dbi) {
         dao = dbi.onDemand(CustomFieldSqlDao.class);
     }
 
diff --git a/util/src/main/java/com/ning/billing/util/dao/AuditBinder.java b/util/src/main/java/com/ning/billing/util/dao/AuditBinder.java
index 8a9a64e..a518578 100644
--- a/util/src/main/java/com/ning/billing/util/dao/AuditBinder.java
+++ b/util/src/main/java/com/ning/billing/util/dao/AuditBinder.java
@@ -37,6 +37,7 @@ public @interface AuditBinder {
             return new Binder<AuditBinder, EntityAudit>() {
                 @Override
                 public void bind(SQLStatement q, AuditBinder bind, EntityAudit audit) {
+                    q.bind("tableName", audit.getTableName().toString());
                     q.bind("recordId", audit.getRecordId());
                     q.bind("changeType", audit.getChangeType().toString());
                 }
diff --git a/util/src/main/java/com/ning/billing/util/dao/AuditedCollectionDaoBase.java b/util/src/main/java/com/ning/billing/util/dao/AuditedCollectionDaoBase.java
index 65bae2d..da6df68 100644
--- a/util/src/main/java/com/ning/billing/util/dao/AuditedCollectionDaoBase.java
+++ b/util/src/main/java/com/ning/billing/util/dao/AuditedCollectionDaoBase.java
@@ -77,7 +77,7 @@ public abstract class AuditedCollectionDaoBase<T extends Entity> implements Audi
         Map<Long, Long> historyRecordIdMap = convertToAuditMap(historyRecordIds);
         List<EntityAudit> entityAudits = convertToAudits(entityHistories, historyRecordIdMap);
 
-        dao.insertAuditFromTransaction(getTableName(), entityAudits, context);
+        dao.insertAuditFromTransaction(entityAudits, context);
     }
 
     @Override
@@ -109,7 +109,7 @@ public abstract class AuditedCollectionDaoBase<T extends Entity> implements Audi
         for (EntityHistory<T> history : histories) {
             Long recordId = history.getValue();
             Long historyRecordId = historyRecordIds.get(recordId);
-            audits.add(new EntityAudit(historyRecordId, history.getChangeType()));
+            audits.add(new EntityAudit(getTableName(), historyRecordId, history.getChangeType()));
         }
 
         return audits;
diff --git a/util/src/main/java/com/ning/billing/util/dao/AuditSqlDao.java b/util/src/main/java/com/ning/billing/util/dao/AuditSqlDao.java
index 72b2db9..d488c6b 100644
--- a/util/src/main/java/com/ning/billing/util/dao/AuditSqlDao.java
+++ b/util/src/main/java/com/ning/billing/util/dao/AuditSqlDao.java
@@ -29,18 +29,16 @@ import java.util.List;
 @ExternalizedSqlViaStringTemplate3
 public interface AuditSqlDao {
     @SqlUpdate
-    public void insertAuditFromTransaction(@TableNameBinder final TableName tableName,
-                                           @AuditBinder final EntityAudit audit,
+    public void insertAuditFromTransaction(@AuditBinder final EntityAudit audit,
                                            @CallContextBinder final CallContext context);
 
     @SqlBatch(transactional = false)
-    public void insertAuditFromTransaction(@TableNameBinder final TableName tableName,
-                                           @AuditBinder final List<EntityAudit> audit,
+    public void insertAuditFromTransaction(@AuditBinder final List<EntityAudit> audit,
                                            @CallContextBinder final CallContext context);
 
     @SqlQuery
-    public Long getRecordId(@TableNameBinder final TableName tableName, @Bind("id") final String id);
+    public Long getRecordId(@Bind("id") final String id);
 
     @SqlQuery
-    public Long getHistoryRecordId(@TableNameBinder final TableName tableName, @Bind("recordId") final Long recordId);
+    public Long getHistoryRecordId(@Bind("recordId") final Long recordId);
 }
diff --git a/util/src/main/java/com/ning/billing/util/dao/EntityAudit.java b/util/src/main/java/com/ning/billing/util/dao/EntityAudit.java
index f417d00..15280fa 100644
--- a/util/src/main/java/com/ning/billing/util/dao/EntityAudit.java
+++ b/util/src/main/java/com/ning/billing/util/dao/EntityAudit.java
@@ -19,14 +19,20 @@ package com.ning.billing.util.dao;
 import com.ning.billing.util.ChangeType;
 
 public class EntityAudit {
+    private final TableName tableName;
     private final Long recordId;
     private final ChangeType changeType;
 
-    public EntityAudit(Long recordId, ChangeType changeType) {
+    public EntityAudit(TableName tableName, Long recordId, ChangeType changeType) {
+        this.tableName = tableName;
         this.recordId = recordId;
         this.changeType = changeType;
     }
 
+    public TableName getTableName() {
+        return tableName;
+    }
+
     public Long getRecordId() {
         return recordId;
     }
diff --git a/util/src/main/java/com/ning/billing/util/dao/MapperBase.java b/util/src/main/java/com/ning/billing/util/dao/MapperBase.java
index 3229863..119b727 100644
--- a/util/src/main/java/com/ning/billing/util/dao/MapperBase.java
+++ b/util/src/main/java/com/ning/billing/util/dao/MapperBase.java
@@ -31,6 +31,7 @@ public abstract class MapperBase {
     }
 
     protected UUID getUUID(ResultSet resultSet, String fieldName) throws SQLException {
-        return UUID.fromString(resultSet.getString(fieldName));
+        String result = resultSet.getString(fieldName);
+        return result == null ? null : UUID.fromString(result);
     }
 }
diff --git a/util/src/main/java/com/ning/billing/util/dao/TableName.java b/util/src/main/java/com/ning/billing/util/dao/TableName.java
index 07749e1..28a070a 100644
--- a/util/src/main/java/com/ning/billing/util/dao/TableName.java
+++ b/util/src/main/java/com/ning/billing/util/dao/TableName.java
@@ -23,11 +23,13 @@ public enum TableName {
     BUNDLES("bundles"),
     CUSTOM_FIELD_HISTORY("custom_field_history"),
     ENTITLEMENT_EVENTS("entitlement_events"),
+    FIXED_INVOICE_ITEMS("fixed_invoice_items"),
     INVOICE_PAYMENTS("invoice_payments"),
     INVOICES("invoices"),
     PAYMENT_ATTEMPTS("payment_attempts"),
     PAYMENT_HISTORY("payment_history"),
     PAYMENTS("payments"),
+    RECURRING_INVOICE_ITEMS("recurring_invoice_items"),
     SUBSCRIPTIONS("subscriptions"),
     TAG_HISTORY("tag_history");
     
diff --git a/util/src/main/java/com/ning/billing/util/email/DefaultEmailSender.java b/util/src/main/java/com/ning/billing/util/email/DefaultEmailSender.java
index 103a87b..b4e3587 100644
--- a/util/src/main/java/com/ning/billing/util/email/DefaultEmailSender.java
+++ b/util/src/main/java/com/ning/billing/util/email/DefaultEmailSender.java
@@ -16,16 +16,16 @@
 
 package com.ning.billing.util.email;
 
-import com.google.inject.Inject;
-import com.ning.billing.ErrorCode;
+import java.util.List;
+
 import org.apache.commons.mail.EmailException;
 import org.apache.commons.mail.HtmlEmail;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import java.util.List;
+import com.google.inject.Inject;
 
-public class DefaultEmailSender implements EmailSender {
+public class DefaultEmailSender implements EmailSender { 
     private final Logger log = LoggerFactory.getLogger(EmailSender.class);
     private final EmailConfig config;
 
diff --git a/util/src/main/java/com/ning/billing/util/entity/dao/EntitySqlDao.java b/util/src/main/java/com/ning/billing/util/entity/dao/EntitySqlDao.java
index dcdfbce..780a34f 100644
--- a/util/src/main/java/com/ning/billing/util/entity/dao/EntitySqlDao.java
+++ b/util/src/main/java/com/ning/billing/util/entity/dao/EntitySqlDao.java
@@ -35,7 +35,7 @@ public interface EntitySqlDao<T extends Entity> {
     public T getById(@Bind("id") final String id);
 
     @SqlQuery
-    public Long getRecordId(@Bind("objectId") final String objectId);
+    public Long getRecordId(@Bind("id") final String id);
 
     @SqlQuery
     public Long getHistoryRecordId(@Bind("recordId") final Long recordId);
diff --git a/util/src/main/java/com/ning/billing/util/entity/dao/UpdatableEntitySqlDao.java b/util/src/main/java/com/ning/billing/util/entity/dao/UpdatableEntitySqlDao.java
index e7ffa15..59bc69e 100644
--- a/util/src/main/java/com/ning/billing/util/entity/dao/UpdatableEntitySqlDao.java
+++ b/util/src/main/java/com/ning/billing/util/entity/dao/UpdatableEntitySqlDao.java
@@ -17,12 +17,13 @@
 package com.ning.billing.util.entity.dao;
 
 import com.ning.billing.util.callcontext.CallContext;
+import com.ning.billing.util.dao.AuditSqlDao;
 import com.ning.billing.util.dao.EntityHistory;
 import com.ning.billing.util.entity.Entity;
 import org.skife.jdbi.v2.sqlobject.SqlUpdate;
 
 // this interface needs to be extended by an interface that provides (externalized) sql and object binders and mappers
-public interface UpdatableEntitySqlDao<T extends Entity> extends EntitySqlDao<T> {
+public interface UpdatableEntitySqlDao<T extends Entity> extends EntitySqlDao<T>, AuditSqlDao {
     @SqlUpdate
     public void update(final T entity, final CallContext context);
 
diff --git a/util/src/main/java/com/ning/billing/util/glue/BusModule.java b/util/src/main/java/com/ning/billing/util/glue/BusModule.java
index 2d1d30b..656253d 100644
--- a/util/src/main/java/com/ning/billing/util/glue/BusModule.java
+++ b/util/src/main/java/com/ning/billing/util/glue/BusModule.java
@@ -29,8 +29,7 @@ public class BusModule extends AbstractModule {
     
     public BusModule() {
         super();
-        // Default to Memory at this point
-        type = BusType.MEMORY;
+        type = BusType.PERSISTENT;        
     }
 
     public BusModule(BusType type) {
diff --git a/util/src/main/java/com/ning/billing/util/notificationq/dao/NotificationSqlDao.java b/util/src/main/java/com/ning/billing/util/notificationq/dao/NotificationSqlDao.java
index f3cecc1..1ae51ee 100644
--- a/util/src/main/java/com/ning/billing/util/notificationq/dao/NotificationSqlDao.java
+++ b/util/src/main/java/com/ning/billing/util/notificationq/dao/NotificationSqlDao.java
@@ -49,14 +49,14 @@ public interface NotificationSqlDao extends Transactional<NotificationSqlDao>, C
     //
     @SqlQuery
     @Mapper(NotificationSqlMapper.class)
-    public List<Notification> getReadyNotifications(@Bind("now") Date now, @Bind("max") int max, @Bind("queueName") String queueName);
+    public List<Notification> getReadyNotifications(@Bind("now") Date now, @Bind("owner") String owner, @Bind("max") int max, @Bind("queueName") String queueName);
 
     @SqlUpdate
     public int claimNotification(@Bind("owner") String owner, @Bind("nextAvailable") Date nextAvailable,
-                                 @Bind("recordId") long id, @Bind("now") Date now);
+                                 @Bind("id") String id, @Bind("now") Date now);
 
     @SqlUpdate
-    public void clearNotification(@Bind("recordId") long id, @Bind("owner") String owner);
+    public void clearNotification(@Bind("id") String id, @Bind("owner") String owner);
 
     @SqlUpdate
     public void removeNotificationsByKey(@Bind("notificationKey") String key);
@@ -65,14 +65,14 @@ public interface NotificationSqlDao extends Transactional<NotificationSqlDao>, C
     public void insertNotification(@Bind(binder = NotificationSqlDaoBinder.class) Notification evt);
 
     @SqlUpdate
-    public void insertClaimedHistory(@Bind("sequenceId") int sequenceId, @Bind("owner") String owner,
-                                     @Bind("claimedDate") Date claimedDate, @Bind("notificationId") String notificationId);
+    public void insertClaimedHistory(@Bind("ownerId") String ownerId, @Bind("claimedDate") Date claimedDate, @Bind("notificationId") String notificationId);
 
     public static class NotificationSqlDaoBinder extends BinderBase implements Binder<Bind, Notification> {
         @Override
         public void bind(@SuppressWarnings("rawtypes") SQLStatement stmt, Bind bind, Notification evt) {
-            stmt.bind("id", evt.getUUID().toString());
+            stmt.bind("id", evt.getId().toString());
             stmt.bind("createdDate", getDate(new DateTime()));
+            stmt.bind("creatingOwner", evt.getCreatedOwner());            
             stmt.bind("notificationKey", evt.getNotificationKey());
             stmt.bind("effectiveDate", getDate(evt.getEffectiveDate()));
             stmt.bind("queueName", evt.getQueueName());
@@ -88,8 +88,9 @@ public interface NotificationSqlDao extends Transactional<NotificationSqlDao>, C
         public Notification map(int index, ResultSet r, StatementContext ctx)
         throws SQLException {
 
-            final Long recordId = r.getLong("record_id");
+            final Long ordering = r.getLong("record_id");
             final UUID id = getUUID(r, "id");
+            final String createdOwner = r.getString("creating_owner");            
             final String notificationKey = r.getString("notification_key");
             final String queueName = r.getString("queue_name");
             final DateTime effectiveDate = getDate(r, "effective_date");
@@ -97,7 +98,7 @@ public interface NotificationSqlDao extends Transactional<NotificationSqlDao>, C
             final String processingOwner = r.getString("processing_owner");
             final NotificationLifecycleState processingState = NotificationLifecycleState.valueOf(r.getString("processing_state"));
 
-            return new DefaultNotification(recordId, id, processingOwner, queueName, nextAvailableDate,
+            return new DefaultNotification(ordering, id, createdOwner, processingOwner, queueName, nextAvailableDate,
                     processingState, notificationKey, effectiveDate);
 
         }
diff --git a/util/src/main/java/com/ning/billing/util/notificationq/DefaultNotification.java b/util/src/main/java/com/ning/billing/util/notificationq/DefaultNotification.java
index 26e6c4e..d6f1453 100644
--- a/util/src/main/java/com/ning/billing/util/notificationq/DefaultNotification.java
+++ b/util/src/main/java/com/ning/billing/util/notificationq/DefaultNotification.java
@@ -18,13 +18,13 @@ package com.ning.billing.util.notificationq;
 
 import java.util.UUID;
 
+import com.ning.billing.util.entity.EntityBase;
 import org.joda.time.DateTime;
 
-public class DefaultNotification implements Notification {
-
-    private final long id;
-    private final UUID uuid;
+public class DefaultNotification extends EntityBase implements Notification {
+    private final long ordering;
     private final String owner;
+    private final String createdOwner;
     private final String queueName;
     private final DateTime nextAvailableDate;
     private final NotificationLifecycleState lifecycleState;
@@ -32,13 +32,13 @@ public class DefaultNotification implements Notification {
     private final DateTime effectiveDate;
 
 
-    public DefaultNotification(long id, UUID uuid, String owner, String queueName, DateTime nextAvailableDate,
+    public DefaultNotification(long ordering, UUID id, String createdOwner, String owner, String queueName, DateTime nextAvailableDate,
             NotificationLifecycleState lifecycleState,
             String notificationKey, DateTime effectiveDate) {
-        super();
-        this.id = id;
-        this.uuid = uuid;
+        super(id);
+        this.ordering = ordering;
         this.owner = owner;
+        this.createdOwner = createdOwner;
         this.queueName = queueName;
         this.nextAvailableDate = nextAvailableDate;
         this.lifecycleState = lifecycleState;
@@ -46,17 +46,12 @@ public class DefaultNotification implements Notification {
         this.effectiveDate = effectiveDate;
     }
 
-    @Override
-    public long getId() {
-        return id;
-    }
-
-    public DefaultNotification(String queueName, String notificationKey, DateTime effectiveDate) {
-        this(-1L, UUID.randomUUID(), null, queueName, null, NotificationLifecycleState.AVAILABLE, notificationKey, effectiveDate);
+    public DefaultNotification(String queueName, String createdOwner, String notificationKey, DateTime effectiveDate) {
+        this(-1L, UUID.randomUUID(), createdOwner, null, queueName, null, NotificationLifecycleState.AVAILABLE, notificationKey, effectiveDate);
     }
     @Override
-    public UUID getUUID() {
-        return uuid;
+    public Long getOrdering() {
+        return ordering;
     }
 
     @Override
@@ -108,4 +103,8 @@ public class DefaultNotification implements Notification {
 		return queueName;
 	}
 
+    @Override
+    public String getCreatedOwner() {
+        return createdOwner;
+    }
 }
diff --git a/util/src/main/java/com/ning/billing/util/notificationq/DefaultNotificationQueue.java b/util/src/main/java/com/ning/billing/util/notificationq/DefaultNotificationQueue.java
index 392a218..4f114b6 100644
--- a/util/src/main/java/com/ning/billing/util/notificationq/DefaultNotificationQueue.java
+++ b/util/src/main/java/com/ning/billing/util/notificationq/DefaultNotificationQueue.java
@@ -41,10 +41,10 @@ public class DefaultNotificationQueue extends NotificationQueueBase {
     }
 
     @Override
-    protected int doProcessEvents(final int sequenceId) {
+    public int doProcessEvents() {
 
         logDebug("ENTER doProcessEvents");
-        List<Notification> notifications = getReadyNotifications(sequenceId);
+        List<Notification> notifications = getReadyNotifications();
         if (notifications.size() == 0) {
             logDebug("EXIT doProcessEvents");
             return 0;
@@ -56,19 +56,19 @@ public class DefaultNotificationQueue extends NotificationQueueBase {
         for (final Notification cur : notifications) {
             nbProcessedEvents.incrementAndGet();
             logDebug("handling notification %s, key = %s for time %s",
-                    cur.getUUID(), cur.getNotificationKey(), cur.getEffectiveDate());
+                    cur.getId(), cur.getNotificationKey(), cur.getEffectiveDate());
             handler.handleReadyNotification(cur.getNotificationKey(), cur.getEffectiveDate());
             result++;
             clearNotification(cur);
             logDebug("done handling notification %s, key = %s for time %s",
-                    cur.getUUID(), cur.getNotificationKey(), cur.getEffectiveDate());
+                    cur.getId(), cur.getNotificationKey(), cur.getEffectiveDate());
         }
         return result;
     }
 
     @Override
     public void recordFutureNotification(DateTime futureNotificationTime, NotificationKey notificationKey) {
-        Notification notification = new DefaultNotification(getFullQName(), notificationKey.toString(), futureNotificationTime);
+        Notification notification = new DefaultNotification(getFullQName(), hostname,  notificationKey.toString(), futureNotificationTime);
         dao.insertNotification(notification);
     }
 
@@ -76,32 +76,32 @@ public class DefaultNotificationQueue extends NotificationQueueBase {
     public void recordFutureNotificationFromTransaction(final Transmogrifier transactionalDao,
             final DateTime futureNotificationTime, final NotificationKey notificationKey) {
         NotificationSqlDao transactionalNotificationDao =  transactionalDao.become(NotificationSqlDao.class);
-        Notification notification = new DefaultNotification(getFullQName(), notificationKey.toString(), futureNotificationTime);
+        Notification notification = new DefaultNotification(getFullQName(), hostname, notificationKey.toString(), futureNotificationTime);
         transactionalNotificationDao.insertNotification(notification);
     }
 
 
     private void clearNotification(final Notification cleared) {
-        dao.clearNotification(cleared.getId(), hostname);
+        dao.clearNotification(cleared.getId().toString(), hostname);
     }
 
-    private List<Notification> getReadyNotifications(final int seqId) {
+    private List<Notification> getReadyNotifications() {
 
         final Date now = clock.getUTCNow().toDate();
         final Date nextAvailable = clock.getUTCNow().plus(config.getDaoClaimTimeMs()).toDate();
 
-        List<Notification> input = dao.getReadyNotifications(now, config.getDaoMaxReadyEvents(), getFullQName());
+        List<Notification> input = dao.getReadyNotifications(now, hostname, config.getDaoMaxReadyEvents(), getFullQName());
 
         List<Notification> claimedNotifications = new ArrayList<Notification>();
         for (Notification cur : input) {
             logDebug("about to claim notification %s,  key = %s for time %s",
-                    cur.getUUID(), cur.getNotificationKey(), cur.getEffectiveDate());
-            final boolean claimed = (dao.claimNotification(hostname, nextAvailable, cur.getId(), now) == 1);
+                    cur.getId(), cur.getNotificationKey(), cur.getEffectiveDate());
+            final boolean claimed = (dao.claimNotification(hostname, nextAvailable, cur.getId().toString(), now) == 1);
             logDebug("claimed notification %s, key = %s for time %s result = %s",
-                    cur.getUUID(), cur.getNotificationKey(), cur.getEffectiveDate(), Boolean.valueOf(claimed));
+                    cur.getId(), cur.getNotificationKey(), cur.getEffectiveDate(), Boolean.valueOf(claimed));
             if (claimed) {
                 claimedNotifications.add(cur);
-                dao.insertClaimedHistory(seqId, hostname, now, cur.getUUID().toString());
+                dao.insertClaimedHistory(hostname, now, cur.getId().toString());
             }
         }
 
diff --git a/util/src/main/java/com/ning/billing/util/notificationq/Notification.java b/util/src/main/java/com/ning/billing/util/notificationq/Notification.java
index d59098b..bd3169a 100644
--- a/util/src/main/java/com/ning/billing/util/notificationq/Notification.java
+++ b/util/src/main/java/com/ning/billing/util/notificationq/Notification.java
@@ -16,22 +16,15 @@
 
 package com.ning.billing.util.notificationq;
 
-import java.util.UUID;
-
+import com.ning.billing.util.entity.Entity;
 import org.joda.time.DateTime;
 
-
-public interface Notification extends NotificationLifecycle {
-
-    public long getId();
-
-    public UUID getUUID();
+public interface Notification extends NotificationLifecycle, Entity {
+    public Long getOrdering();
 
     public String getNotificationKey();
 
     public DateTime getEffectiveDate();
 
     public String getQueueName();
-
-
 }
diff --git a/util/src/main/java/com/ning/billing/util/notificationq/NotificationLifecycle.java b/util/src/main/java/com/ning/billing/util/notificationq/NotificationLifecycle.java
index 32cba9d..0110cf5 100644
--- a/util/src/main/java/com/ning/billing/util/notificationq/NotificationLifecycle.java
+++ b/util/src/main/java/com/ning/billing/util/notificationq/NotificationLifecycle.java
@@ -29,6 +29,8 @@ public interface NotificationLifecycle {
     }
 
     public String getOwner();
+    
+    public String getCreatedOwner();
 
     public DateTime getNextAvailableDate();
 
diff --git a/util/src/main/java/com/ning/billing/util/notificationq/NotificationQueue.java b/util/src/main/java/com/ning/billing/util/notificationq/NotificationQueue.java
index 14b68e0..d5c2425 100644
--- a/util/src/main/java/com/ning/billing/util/notificationq/NotificationQueue.java
+++ b/util/src/main/java/com/ning/billing/util/notificationq/NotificationQueue.java
@@ -22,8 +22,9 @@ import org.joda.time.DateTime;
 import org.skife.jdbi.v2.sqlobject.mixins.Transmogrifier;
 
 import com.ning.billing.util.notificationq.NotificationQueueService.NotificationQueueHandler;
+import com.ning.billing.util.queue.QueueLifecycle;
 
-public interface NotificationQueue {
+public interface NotificationQueue extends QueueLifecycle {
 
    /**
     *
@@ -61,25 +62,9 @@ public interface NotificationQueue {
    public int processReadyNotification();
 
    /**
-    * Stops the queue. Blocks until queue is completely stopped.
-    *
-    * @see NotificationQueueHandler.completedQueueStop to be notified when the notification thread exited
-    */
-   public void stopQueue();
-
-   /**
-    * Starts the queue. Blocks until queue has completely started.
-    *
-    * @see NotificationQueueHandler.completedQueueStart to be notified when the notification thread started
-    */
-   public void startQueue();
-
-   /**
     *
     * @return the name of that queue
     */
    public String getFullQName();
 
-   
-
 }
diff --git a/util/src/main/java/com/ning/billing/util/notificationq/NotificationQueueBase.java b/util/src/main/java/com/ning/billing/util/notificationq/NotificationQueueBase.java
index eb3f269..7c82db6 100644
--- a/util/src/main/java/com/ning/billing/util/notificationq/NotificationQueueBase.java
+++ b/util/src/main/java/com/ning/billing/util/notificationq/NotificationQueueBase.java
@@ -17,10 +17,8 @@
 package com.ning.billing.util.notificationq;
 
 import java.lang.Thread.UncaughtExceptionHandler;
-import java.util.concurrent.Executor;
 import java.util.concurrent.Executors;
 import java.util.concurrent.ThreadFactory;
-import java.util.concurrent.atomic.AtomicInteger;
 import java.util.concurrent.atomic.AtomicLong;
 
 import org.slf4j.Logger;
@@ -30,48 +28,30 @@ import com.ning.billing.config.NotificationConfig;
 import com.ning.billing.util.Hostname;
 import com.ning.billing.util.clock.Clock;
 import com.ning.billing.util.notificationq.NotificationQueueService.NotificationQueueHandler;
+import com.ning.billing.util.queue.PersistentQueueBase;
 
 
-public abstract class NotificationQueueBase implements NotificationQueue {
+public abstract class NotificationQueueBase extends PersistentQueueBase implements NotificationQueue {
 
     protected final static Logger log = LoggerFactory.getLogger(NotificationQueueBase.class);
 
-    private static final long MAX_NOTIFICATION_THREAD_WAIT_MS = 10000; // 10 secs
-    private static final long NOTIFICATION_THREAD_WAIT_INCREMENT_MS = 1000; // 1 sec
-    private static final long NANO_TO_MS = (1000 * 1000);
-
     protected static final String NOTIFICATION_THREAD_PREFIX = "Notification-";
-    protected final long STOP_WAIT_TIMEOUT_MS = 60000;
+    protected static final long STOP_WAIT_TIMEOUT_MS = 60000;
 
     protected final String svcName;
     protected final String queueName;
     protected final NotificationQueueHandler handler;
     protected final NotificationConfig config;
 
-    protected final Executor executor;
     protected final Clock clock;
     protected final String hostname;
 
-    protected static final AtomicInteger sequenceId = new AtomicInteger();
-
     protected AtomicLong nbProcessedEvents;
 
-    // Use this object's monitor for synchronization (no need for volatile)
-    protected boolean isProcessingEvents;
-
-    private boolean startedComplete = false;
-    private boolean stoppedComplete = false;
-
     // Package visibility on purpose
     NotificationQueueBase(final Clock clock,  final String svcName, final String queueName, final NotificationQueueHandler handler, final NotificationConfig config) {
-        this.clock = clock;
-        this.svcName = svcName;
-        this.queueName = queueName;
-        this.handler = handler;
-        this.config = config;
-        this.hostname = Hostname.get();
+        super(svcName, Executors.newFixedThreadPool(1, new ThreadFactory() {
 
-        this.executor = Executors.newSingleThreadExecutor(new ThreadFactory() {
             @Override
             public Thread newThread(Runnable r) {
                 Thread th = new Thread(r);
@@ -84,103 +64,21 @@ public abstract class NotificationQueueBase implements NotificationQueue {
                 });
                 return th;
             }
-        });
-    }
-
+        }), 1, STOP_WAIT_TIMEOUT_MS, config.getNotificationSleepTimeMs());
 
-    @Override
-    public int processReadyNotification() {
-        return doProcessEvents(sequenceId.incrementAndGet());
+        this.clock = clock;
+        this.svcName = svcName;
+        this.queueName = queueName;
+        this.handler = handler;
+        this.config = config;
+        this.hostname = Hostname.get();
+        this.nbProcessedEvents = new AtomicLong();
     }
 
 
     @Override
-    public void stopQueue() {
-        if (config.isNotificationProcessingOff()) {
-            completedQueueStop();
-            return;
-        }
-
-        synchronized(this) {
-            isProcessingEvents = false;
-            try {
-                log.info("NotificationQueue requested to stop");
-                wait(STOP_WAIT_TIMEOUT_MS);
-                log.info("NotificationQueue requested should have exited");
-            } catch (InterruptedException e) {
-                log.warn("NotificationQueue got interrupted exception when stopping notifications", e);
-            }
-        }
-        waitForNotificationStopCompletion();
-    }
-
-    @Override
-    public void startQueue() {
-
-        this.isProcessingEvents = true;
-        this.nbProcessedEvents = new AtomicLong();
-
-
-        if (config.isNotificationProcessingOff()) {
-            log.warn(String.format("KILLBILL NOTIFICATION PROCESSING FOR SVC %s IS OFF !!!", getFullQName()));
-            completedQueueStart();
-            return;
-        }
-        final NotificationQueueBase notificationQueue = this;
-
-        executor.execute(new Runnable() {
-            @Override
-            public void run() {
-
-                log.info(String.format("NotificationQueue thread %s [%d] started",
-                        Thread.currentThread().getName(),
-                        Thread.currentThread().getId()));
-
-                // Thread is now started, notify the listener
-                completedQueueStart();
-
-                try {
-                    while (true) {
-
-                        synchronized (notificationQueue) {
-                            if (!isProcessingEvents) {
-                                log.info(String.format("NotificationQueue has been requested to stop, thread  %s  [%d] stopping...",
-                                        Thread.currentThread().getName(),
-                                        Thread.currentThread().getId()));
-                                notificationQueue.notify();
-                                break;
-                            }
-                        }
-
-                        // Callback may trigger exceptions in user code so catch anything here and live with it.
-                        try {
-                            doProcessEvents(sequenceId.getAndIncrement());
-                        } catch (Exception e) {
-                            log.error(String.format("NotificationQueue thread  %s  [%d] got an exception..",
-                                    Thread.currentThread().getName(),
-                                    Thread.currentThread().getId()), e);
-                        }
-                        sleepALittle();
-                    }
-                } catch (InterruptedException e) {
-                    log.warn(Thread.currentThread().getName() + " got interrupted ", e);
-                } catch (Throwable e) {
-                    log.error(Thread.currentThread().getName() + " got an exception exiting...", e);
-                    // Just to make it really obvious in the log
-                    e.printStackTrace();
-                } finally {
-                    completedQueueStop();
-                    log.info(String.format("NotificationQueue thread  %s  [%d] exited...",
-                            Thread.currentThread().getName(),
-                            Thread.currentThread().getId()));
-                }
-            }
-
-            private void sleepALittle() throws InterruptedException {
-                Thread.sleep(config.getNotificationSleepTimeMs());
-            }
-        });
-        waitForNotificationStartCompletion();
+    public int processReadyNotification() {
+        return doProcessEvents();
     }
 
     @Override
@@ -188,61 +86,12 @@ public abstract class NotificationQueueBase implements NotificationQueue {
         return getFullQName();
     }
 
-    private void completedQueueStop() {
-    	synchronized (this) {
-    		stoppedComplete = true;
-            this.notifyAll();
-        }
-    }
-
-    private void completedQueueStart() {
-        synchronized (this) {
-        	startedComplete = true;
-            this.notifyAll();
-        }
-    }
-
-    private void waitForNotificationStartCompletion() {
-        waitForNotificationEventCompletion(true);
-    }
-
-    private void waitForNotificationStopCompletion() {
-        waitForNotificationEventCompletion(false);
-    }
-
-    private void waitForNotificationEventCompletion(boolean startEvent) {
-
-        long ini = System.nanoTime();
-        synchronized(this) {
-            do {
-                if ((startEvent ? startedComplete : stoppedComplete)) {
-                    break;
-                }
-                try {
-                    this.wait(NOTIFICATION_THREAD_WAIT_INCREMENT_MS);
-                } catch (InterruptedException e ) {
-                    Thread.currentThread().interrupt();
-                    throw new NotificationError(e);
-                }
-            } while (!(startEvent ? startedComplete : stoppedComplete) &&
-                    (System.nanoTime() - ini) / NANO_TO_MS < MAX_NOTIFICATION_THREAD_WAIT_MS);
-
-            if (!(startEvent ? startedComplete : stoppedComplete)) {
-                log.error("Could not {} notification thread in {} msec !!!",
-                        (startEvent ? "start" : "stop"),
-                        MAX_NOTIFICATION_THREAD_WAIT_MS);
-                throw new NotificationError("Failed to start service!!");
-            }
-            log.info("Notification thread has been {} in {} ms",
-                    (startEvent ? "started" : "stopped"),
-                    (System.nanoTime() - ini) / NANO_TO_MS);
-        }
-    }
 
     @Override
     public String getFullQName() {
         return svcName + ":" +  queueName;
     }
 
-    protected abstract int doProcessEvents(int sequenceId);
+    @Override
+    public abstract int doProcessEvents();
 }
diff --git a/util/src/main/java/com/ning/billing/util/queue/PersistentQueueBase.java b/util/src/main/java/com/ning/billing/util/queue/PersistentQueueBase.java
new file mode 100644
index 0000000..a6844ad
--- /dev/null
+++ b/util/src/main/java/com/ning/billing/util/queue/PersistentQueueBase.java
@@ -0,0 +1,159 @@
+/* 
+ * 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.util.queue;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.Executor;
+import java.util.concurrent.TimeUnit;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+public abstract class PersistentQueueBase implements QueueLifecycle {
+
+    private static final Logger log = LoggerFactory.getLogger(PersistentQueueBase.class);
+
+    private final int nbThreads;
+    private final Executor executor;
+    private final String svcName;
+    private final long sleepTimeMs;
+    private final long waitTimeoutMs;
+
+    private boolean isProcessingEvents;
+    private int curActiveThreads;
+    
+    public PersistentQueueBase(final String svcName, final Executor executor, final int nbThreads, final long waitTimeoutMs, final long sleepTimeMs) {
+        this.executor = executor;
+        this.nbThreads = nbThreads;
+        this.svcName = svcName;
+        this.waitTimeoutMs = waitTimeoutMs;
+        this.sleepTimeMs = sleepTimeMs;
+        this.isProcessingEvents = false;
+        this.curActiveThreads = 0;
+    }
+    
+    @Override
+    public void startQueue() {
+        
+        isProcessingEvents = true;
+        curActiveThreads = 0;
+        
+        final PersistentQueueBase thePersistentQ = this;
+        final CountDownLatch doneInitialization = new CountDownLatch(nbThreads);
+
+        log.info(String.format("%s: Starting with %d threads",
+                svcName, nbThreads));
+        
+        for (int i = 0; i < nbThreads; i++) {
+            executor.execute(new Runnable() {
+                @Override
+                public void run() {
+
+                    log.info(String.format("%s: Thread %s [%d] starting",
+                            svcName,
+                            Thread.currentThread().getName(),
+                            Thread.currentThread().getId()));
+                    
+                    synchronized(thePersistentQ) {
+                        curActiveThreads++;
+                    }
+
+                    doneInitialization.countDown();
+                    
+                    try {
+                        while (true) {
+                            
+                            synchronized(thePersistentQ) {
+                                if (!isProcessingEvents) {
+                                    thePersistentQ.notify();
+                                    break;
+                                }
+                            }
+
+                            try {
+                                doProcessEvents();
+                            } catch (Exception e) {
+                                log.warn(String.format("%s: Thread  %s  [%d] got an exception, catching and moving on...",
+                                        svcName,
+                                        Thread.currentThread().getName(),
+                                        Thread.currentThread().getId()), e);
+                            }
+                            sleepALittle();
+                        }
+                    } catch (InterruptedException e) {
+                        log.info(String.format("%s: Thread %s got interrupted, exting... ", svcName, Thread.currentThread().getName()));
+                    } catch (Throwable e) {
+                        log.error(String.format("%s: Thread %s got an exception, exting... ", svcName, Thread.currentThread().getName()), e);                        
+                    } finally {
+
+                        log.info(String.format("%s: Thread %s has exited", svcName, Thread.currentThread().getName()));                                                
+                        synchronized(thePersistentQ) {
+                            curActiveThreads--;
+                        }
+                    }
+                }
+                
+                private void sleepALittle() throws InterruptedException {
+                    Thread.sleep(sleepTimeMs);
+                }
+            });
+        }
+        try {
+            boolean success = doneInitialization.await(sleepTimeMs, TimeUnit.MILLISECONDS);
+            if (!success) {
+                
+                log.warn(String.format("%s: Failed to wait for all threads to be started, got %d/%d", svcName, (nbThreads - doneInitialization.getCount()), nbThreads));
+            } else {
+                log.info(String.format("%s: Done waiting for all threads to be started, got %d/%d", svcName, (nbThreads - doneInitialization.getCount()), nbThreads));                
+            }
+        } catch (InterruptedException e) {
+            log.warn(String.format("%s: Start sequence, got interrupted", svcName));
+        }
+    }
+    
+    
+    @Override
+    public void stopQueue() {
+        int remaining = 0;
+        try {
+            synchronized(this) {
+                isProcessingEvents = false;
+                long ini = System.currentTimeMillis();
+                long remainingWaitTimeMs = waitTimeoutMs;
+                while (curActiveThreads > 0 && remainingWaitTimeMs > 0) {
+                    wait(1000);
+                    remainingWaitTimeMs = waitTimeoutMs - (System.currentTimeMillis() - ini);
+                }
+                remaining = curActiveThreads;
+            }
+            
+        } catch (InterruptedException ignore) {
+            log.info(String.format("%s: Stop sequence has been interrupted, remaining active threads = %d", svcName, curActiveThreads));
+        } finally {
+            if (remaining > 0) {
+                log.error(String.format("%s: Stop sequence completed with %d active remaing threads", svcName, curActiveThreads));
+            } else {
+                log.info(String.format("%s: Stop sequence completed with %d active remaing threads", svcName, curActiveThreads));                
+            }
+            curActiveThreads = 0;
+        }
+    }
+    
+    
+    @Override
+    public abstract int doProcessEvents();
+}
diff --git a/util/src/main/java/com/ning/billing/util/tag/dao/AuditedTagDao.java b/util/src/main/java/com/ning/billing/util/tag/dao/AuditedTagDao.java
index 4a5741d..a9a2373 100644
--- a/util/src/main/java/com/ning/billing/util/tag/dao/AuditedTagDao.java
+++ b/util/src/main/java/com/ning/billing/util/tag/dao/AuditedTagDao.java
@@ -72,7 +72,7 @@ public class AuditedTagDao extends AuditedCollectionDaoBase<Tag> implements TagD
                 List<Mapper<Long, Long>> historyRecordIds = tagSqlDao.getHistoryRecordIds(maxHistoryRecordId);
                 Map<Long, Long> historyRecordIdMap = convertToAuditMap(historyRecordIds);
                 List<EntityAudit> entityAudits = convertToAudits(entityHistories, historyRecordIdMap);
-                tagSqlDao.insertAuditFromTransaction(getTableName(), entityAudits, context);
+                tagSqlDao.insertAuditFromTransaction(entityAudits, context);
 
                 return null;
             }
@@ -108,7 +108,7 @@ public class AuditedTagDao extends AuditedCollectionDaoBase<Tag> implements TagD
                 List<Mapper<Long, Long>> historyRecordIds = tagSqlDao.getHistoryRecordIds(maxHistoryRecordId);
                 Map<Long, Long> historyRecordIdMap = convertToAuditMap(historyRecordIds);
                 List<EntityAudit> entityAudits = convertToAudits(entityHistories, historyRecordIdMap);
-                tagSqlDao.insertAuditFromTransaction(getTableName(), entityAudits, context);
+                tagSqlDao.insertAuditFromTransaction(entityAudits, context);
 
                 return null;
             }
diff --git a/util/src/main/java/com/ning/billing/util/tag/dao/TagBinder.java b/util/src/main/java/com/ning/billing/util/tag/dao/TagBinder.java
index 2416b21..d2d4a6f 100644
--- a/util/src/main/java/com/ning/billing/util/tag/dao/TagBinder.java
+++ b/util/src/main/java/com/ning/billing/util/tag/dao/TagBinder.java
@@ -22,8 +22,6 @@ import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.lang.annotation.Target;
 
-import com.google.inject.Inject;
-import com.ning.billing.util.clock.Clock;
 import org.skife.jdbi.v2.SQLStatement;
 import org.skife.jdbi.v2.sqlobject.Binder;
 import org.skife.jdbi.v2.sqlobject.BinderFactory;
diff --git a/util/src/main/resources/com/ning/billing/util/bus/dao/PersistentBusSqlDao.sql.stg b/util/src/main/resources/com/ning/billing/util/bus/dao/PersistentBusSqlDao.sql.stg
index de6ce41..0cbd4ec 100644
--- a/util/src/main/resources/com/ning/billing/util/bus/dao/PersistentBusSqlDao.sql.stg
+++ b/util/src/main/resources/com/ning/billing/util/bus/dao/PersistentBusSqlDao.sql.stg
@@ -6,6 +6,7 @@ getNextBusEventEntry() ::= <<
       , class_name
       , event_json
       , created_date
+      , creating_owner
       , processing_owner
       , processing_available_date
       , processing_state
@@ -59,6 +60,7 @@ insertBusEvent() ::= <<
       class_name
     , event_json
     , created_date
+    , creating_owner
     , processing_owner
     , processing_available_date
     , processing_state
@@ -66,6 +68,7 @@ insertBusEvent() ::= <<
       :className
     , :eventJson
     , :createdDate
+    , :creatingOwner
     , :processingOwner
     , :processingAvailableDate
     , :processingState
diff --git a/util/src/main/resources/com/ning/billing/util/ddl.sql b/util/src/main/resources/com/ning/billing/util/ddl.sql
index ebade12..9ea7c1a 100644
--- a/util/src/main/resources/com/ning/billing/util/ddl.sql
+++ b/util/src/main/resources/com/ning/billing/util/ddl.sql
@@ -103,6 +103,7 @@ CREATE TABLE notifications (
     id char(36) NOT NULL,
     created_date datetime NOT NULL,
 	notification_key varchar(256) NOT NULL,
+	creating_owner char(50) NOT NULL,
     effective_date datetime NOT NULL,
     queue_name char(64) NOT NULL,
     processing_owner char(50) DEFAULT NULL,
@@ -118,7 +119,6 @@ CREATE INDEX  `idx_get_ready` ON notifications (`effective_date`,`created_date`,
 DROP TABLE IF EXISTS claimed_notifications;
 CREATE TABLE claimed_notifications (
     record_id int(11) unsigned NOT NULL AUTO_INCREMENT,
-    sequence_id int(11) unsigned NOT NULL,
     owner_id varchar(64) NOT NULL,
     claimed_date datetime NOT NULL,
     notification_id char(36) NOT NULL,
@@ -145,8 +145,9 @@ DROP TABLE IF EXISTS bus_events;
 CREATE TABLE bus_events (
     record_id int(11) unsigned NOT NULL AUTO_INCREMENT,
     class_name varchar(128) NOT NULL, 
-    event_json varchar(1024) NOT NULL,     
+    event_json varchar(2048) NOT NULL,     
     created_date datetime NOT NULL,
+    creating_owner char(50) NOT NULL,
     processing_owner char(50) DEFAULT NULL,
     processing_available_date datetime DEFAULT NULL,
     processing_state varchar(14) DEFAULT 'AVAILABLE',
diff --git a/util/src/main/resources/com/ning/billing/util/notificationq/dao/NotificationSqlDao.sql.stg b/util/src/main/resources/com/ning/billing/util/notificationq/dao/NotificationSqlDao.sql.stg
index 0ea9bd6..0731adc 100644
--- a/util/src/main/resources/com/ning/billing/util/notificationq/dao/NotificationSqlDao.sql.stg
+++ b/util/src/main/resources/com/ning/billing/util/notificationq/dao/NotificationSqlDao.sql.stg
@@ -6,6 +6,7 @@ getReadyNotifications() ::= <<
       , id
       , notification_key
       , created_date
+      , creating_owner
       , effective_date
       , queue_name
       , processing_owner
@@ -26,7 +27,6 @@ getReadyNotifications() ::= <<
     ;
 >>
 
-
 claimNotification() ::= <<
     update notifications
     set
@@ -34,7 +34,7 @@ claimNotification() ::= <<
       , processing_available_date = :nextAvailable
       , processing_state = 'IN_PROCESSING'
     where
-      record_id = :recordId
+      id = :id
       and processing_state != 'PROCESSED'
       and processing_state != 'REMOVED'
       and (processing_owner IS NULL OR processing_available_date \<= :now)
@@ -46,7 +46,7 @@ clearNotification() ::= <<
     set
       processing_state = 'PROCESSED'
     where
-      record_id = :recordId
+      id = :id
     ;
 >>
 
@@ -59,12 +59,12 @@ removeNotificationsByKey() ::= <<
     ;
 >>
 
-
 insertNotification() ::= <<
     insert into notifications (
       id
       , notification_key
       , created_date
+      , creating_owner
       , effective_date
       , queue_name
       , processing_owner
@@ -74,6 +74,7 @@ insertNotification() ::= <<
       :id
       , :notificationKey
       , :createdDate
+      , :creatingOwner
       , :effectiveDate
       , :queueName
       , :processingOwner
@@ -82,16 +83,13 @@ insertNotification() ::= <<
     );   
 >>
 
-
 insertClaimedHistory() ::= <<
     insert into claimed_notifications (
-        sequence_id
-        , owner_id
+          owner_id
         , claimed_date
         , notification_id
       ) values (
-        :sequenceId
-        , :owner
+          :ownerId
         , :claimedDate
         , :notificationId
       );
diff --git a/util/src/test/java/com/ning/billing/dbi/MysqlTestingHelper.java b/util/src/test/java/com/ning/billing/dbi/MysqlTestingHelper.java
index 1b0d9f6..c68c759 100644
--- a/util/src/test/java/com/ning/billing/dbi/MysqlTestingHelper.java
+++ b/util/src/test/java/com/ning/billing/dbi/MysqlTestingHelper.java
@@ -158,10 +158,14 @@ public class MysqlTestingHelper
     
     public void stopMysql()
     {
-        if (mysqldResource != null) {
-            mysqldResource.shutdown();
-            FileUtils.deleteQuietly(dbDir);
-            log.info("MySQLd stopped");
+        try {
+            if (mysqldResource != null) {
+                mysqldResource.shutdown();
+                FileUtils.deleteQuietly(dbDir);
+                log.info("MySQLd stopped");
+            }
+        } catch (Exception ex) {
+            //fail silently
         }
     }
 
diff --git a/util/src/test/java/com/ning/billing/mock/glue/MockDbHelperModule.java b/util/src/test/java/com/ning/billing/mock/glue/MockDbHelperModule.java
index 0487e0d..232c4e8 100644
--- a/util/src/test/java/com/ning/billing/mock/glue/MockDbHelperModule.java
+++ b/util/src/test/java/com/ning/billing/mock/glue/MockDbHelperModule.java
@@ -32,7 +32,7 @@ public class MockDbHelperModule extends AbstractModule {
         installMysqlTestingHelper();
     }
     
-    protected void  installMysqlTestingHelper() {
+    public void  installMysqlTestingHelper() {
 
         final MysqlTestingHelper helper = new MysqlTestingHelper();
         bind(MysqlTestingHelper.class).toInstance(helper);
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 4a813c4..044618e 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
@@ -20,25 +20,43 @@ import com.google.inject.AbstractModule;
 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.user.EntitlementUserApi;
+import com.ning.billing.glue.EntitlementModule;
 import com.ning.billing.mock.BrainDeadProxyFactory;
 import com.ning.billing.util.glue.RealImplementation;
 
-public class MockEntitlementModule extends AbstractModule {
-   
+public class MockEntitlementModule extends AbstractModule implements EntitlementModule {
     
-    protected void installEntitlementService() {
+    /* (non-Javadoc)
+     * @see com.ning.billing.mock.glue.EntitlementModule#installEntitlementService()
+     */
+    @Override
+    public void installEntitlementService() {
         bind(EntitlementService.class).toInstance(BrainDeadProxyFactory.createBrainDeadProxyFor(EntitlementService.class));
     }
-    protected void installEntitlementUserApi() {
+    
+    /* (non-Javadoc)
+     * @see com.ning.billing.mock.glue.EntitlementModule#installEntitlementUserApi()
+     */
+    @Override
+    public void installEntitlementUserApi() {
         bind(EntitlementUserApi.class).annotatedWith(RealImplementation.class).toInstance(BrainDeadProxyFactory.createBrainDeadProxyFor(EntitlementUserApi.class));
     }
     
-    protected void installEntitlementMigrationApi() {
+    /* (non-Javadoc)
+     * @see com.ning.billing.mock.glue.EntitlementModule#installEntitlementMigrationApi()
+     */
+    @Override
+    public void installEntitlementMigrationApi() {
         bind(EntitlementMigrationApi.class).toInstance(BrainDeadProxyFactory.createBrainDeadProxyFor(EntitlementMigrationApi.class));
     }
     
-    protected void installChargeThruApi() {
+    /* (non-Javadoc)
+     * @see com.ning.billing.mock.glue.EntitlementModule#installChargeThruApi()
+     */
+    @Override
+    public void installChargeThruApi() {
         bind(ChargeThruApi.class).toInstance(BrainDeadProxyFactory.createBrainDeadProxyFor(ChargeThruApi.class));
     }
 
@@ -48,5 +66,11 @@ public class MockEntitlementModule extends AbstractModule {
         installEntitlementUserApi();
         installEntitlementMigrationApi();
         installChargeThruApi();
+        installEntitlementTimelineApi();
+    }
+
+    @Override
+    public void installEntitlementTimelineApi() {
+        bind(EntitlementTimelineApi.class).toInstance(BrainDeadProxyFactory.createBrainDeadProxyFor(EntitlementTimelineApi.class));
     }
 }
diff --git a/util/src/test/java/com/ning/billing/mock/glue/MockInvoiceModule.java b/util/src/test/java/com/ning/billing/mock/glue/MockInvoiceModule.java
new file mode 100644
index 0000000..f499e9a
--- /dev/null
+++ b/util/src/test/java/com/ning/billing/mock/glue/MockInvoiceModule.java
@@ -0,0 +1,57 @@
+/*
+ * 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.mock.glue;
+
+import com.google.inject.AbstractModule;
+import com.ning.billing.glue.InvoiceModule;
+import com.ning.billing.invoice.api.InvoiceMigrationApi;
+import com.ning.billing.invoice.api.InvoicePaymentApi;
+import com.ning.billing.invoice.api.InvoiceUserApi;
+import com.ning.billing.invoice.api.test.InvoiceTestApi;
+import com.ning.billing.mock.BrainDeadProxyFactory;
+
+public class MockInvoiceModule extends AbstractModule implements InvoiceModule {
+
+    @Override
+    public void installInvoiceUserApi() {
+       bind(InvoiceUserApi.class).toInstance(BrainDeadProxyFactory.createBrainDeadProxyFor(InvoiceUserApi.class));
+    }
+
+    @Override
+    public void installInvoicePaymentApi() {
+        bind(InvoicePaymentApi.class).toInstance(BrainDeadProxyFactory.createBrainDeadProxyFor(InvoicePaymentApi.class));
+    }
+
+    @Override
+    public void installInvoiceMigrationApi() {
+        bind(InvoiceMigrationApi.class).toInstance(BrainDeadProxyFactory.createBrainDeadProxyFor(InvoiceMigrationApi.class));
+    }
+
+    @Override
+    public void installInvoiceTestApi() {
+        bind(InvoiceTestApi.class).toInstance(BrainDeadProxyFactory.createBrainDeadProxyFor(InvoiceTestApi.class));
+    }
+
+    @Override
+    protected void configure() {
+        installInvoiceUserApi();
+        installInvoicePaymentApi();
+        installInvoiceMigrationApi();
+        installInvoiceTestApi();
+    }
+
+}
diff --git a/util/src/test/java/com/ning/billing/mock/glue/MockJunctionModule.java b/util/src/test/java/com/ning/billing/mock/glue/MockJunctionModule.java
index d21806d..1549c0b 100644
--- a/util/src/test/java/com/ning/billing/mock/glue/MockJunctionModule.java
+++ b/util/src/test/java/com/ning/billing/mock/glue/MockJunctionModule.java
@@ -19,11 +19,12 @@ package com.ning.billing.mock.glue;
 import com.google.inject.AbstractModule;
 import com.ning.billing.account.api.AccountUserApi;
 import com.ning.billing.entitlement.api.user.EntitlementUserApi;
+import com.ning.billing.glue.JunctionModule;
 import com.ning.billing.junction.api.BillingApi;
 import com.ning.billing.junction.api.BlockingApi;
 import com.ning.billing.mock.BrainDeadProxyFactory;
 
-public class MockJunctionModule extends AbstractModule {
+public class MockJunctionModule extends AbstractModule implements JunctionModule {
     private BillingApi billingApi = BrainDeadProxyFactory.createBrainDeadProxyFor(BillingApi.class);
     private BlockingApi blockingApi = BrainDeadProxyFactory.createBrainDeadProxyFor(BlockingApi.class);
     private AccountUserApi userApi = BrainDeadProxyFactory.createBrainDeadProxyFor(AccountUserApi.class);
@@ -37,20 +38,24 @@ public class MockJunctionModule extends AbstractModule {
         installEntitlementUserApi();
     }
 
-    protected void installBillingApi() {
+    @Override
+    public void installBillingApi() {
         bind(BillingApi.class).toInstance(billingApi);
     }
     
     
-    protected void installAccountUserApi() {
+    @Override
+    public void installAccountUserApi() {
         bind(AccountUserApi.class).toInstance(userApi);
     }
     
-    protected void installBlockingApi() {
+    @Override
+    public void installBlockingApi() {
         bind(BlockingApi.class).toInstance(blockingApi);
     }
     
-    protected void installEntitlementUserApi() {
+    @Override
+    public void installEntitlementUserApi() {
         bind(EntitlementUserApi.class).toInstance(entUserApi);
     }
 }
diff --git a/util/src/test/java/com/ning/billing/mock/glue/TestDbiModule.java b/util/src/test/java/com/ning/billing/mock/glue/TestDbiModule.java
new file mode 100644
index 0000000..c42717d
--- /dev/null
+++ b/util/src/test/java/com/ning/billing/mock/glue/TestDbiModule.java
@@ -0,0 +1,43 @@
+/*
+ * 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.mock.glue;
+
+import org.skife.config.ConfigurationObjectFactory;
+import org.skife.jdbi.v2.IDBI;
+
+import com.google.inject.AbstractModule;
+import com.ning.billing.dbi.DBIProvider;
+import com.ning.billing.dbi.DbiConfig;
+import com.ning.billing.dbi.MysqlTestingHelper;
+
+public class TestDbiModule extends AbstractModule {
+ 
+    protected void configure() {
+
+        final MysqlTestingHelper helper = new MysqlTestingHelper();
+        bind(MysqlTestingHelper.class).toInstance(helper);
+        if (helper.isUsingLocalInstance()) {
+            bind(IDBI.class).toProvider(DBIProvider.class).asEagerSingleton();
+            final DbiConfig config = new ConfigurationObjectFactory(System.getProperties()).build(DbiConfig.class);
+            bind(DbiConfig.class).toInstance(config);
+        } else {
+            final IDBI dbi = helper.getDBI(); 
+            bind(IDBI.class).toInstance(dbi);
+        }
+
+     }
+}
diff --git a/util/src/test/java/com/ning/billing/mock/MockAccountBuilder.java b/util/src/test/java/com/ning/billing/mock/MockAccountBuilder.java
new file mode 100644
index 0000000..8b9c2ee
--- /dev/null
+++ b/util/src/test/java/com/ning/billing/mock/MockAccountBuilder.java
@@ -0,0 +1,383 @@
+/*
+ * 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.mock;
+
+import java.util.List;
+import java.util.UUID;
+
+import org.apache.commons.lang.NotImplementedException;
+import org.joda.time.DateTime;
+import org.joda.time.DateTimeZone;
+
+import com.ning.billing.account.api.Account;
+import com.ning.billing.account.api.MutableAccountData;
+import com.ning.billing.catalog.api.Currency;
+import com.ning.billing.junction.api.BlockingState;
+import com.ning.billing.util.callcontext.CallContext;
+import com.ning.billing.util.customfield.CustomField;
+import com.ning.billing.util.dao.ObjectType;
+import com.ning.billing.util.tag.ControlTagType;
+import com.ning.billing.util.tag.Tag;
+import com.ning.billing.util.tag.TagDefinition;
+
+public class MockAccountBuilder {
+    private final UUID id;
+    private String externalKey;
+    private String email;
+    private String name;
+    private int firstNameLength;
+    private Currency currency;
+    private int billingCycleDay;
+    private String paymentProviderName;
+    private DateTimeZone timeZone;
+    private String locale;
+    private String address1;
+    private String address2;
+    private String companyName;
+    private String city;
+    private String stateOrProvince;
+    private String country;
+    private String postalCode;
+    private String phone;
+    private boolean migrated;
+    private boolean isNotifiedForInvoices;
+
+    public MockAccountBuilder() {
+        this(UUID.randomUUID());
+    }
+
+    public MockAccountBuilder(final UUID id) {
+        this.id = id;
+    }
+
+    public MockAccountBuilder externalKey(final String externalKey) {
+        this.externalKey = externalKey;
+        return this;
+    }
+
+    public MockAccountBuilder email(final String email) {
+        this.email = email;
+        return this;
+    }
+
+    public MockAccountBuilder name(final String name) {
+        this.name = name;
+        return this;
+    }
+
+    public MockAccountBuilder firstNameLength(final int firstNameLength) {
+        this.firstNameLength = firstNameLength;
+        return this;
+    }
+
+    public MockAccountBuilder billingCycleDay(final int billingCycleDay) {
+        this.billingCycleDay = billingCycleDay;
+        return this;
+    }
+
+    public MockAccountBuilder currency(final Currency currency) {
+        this.currency = currency;
+        return this;
+    }
+
+    public MockAccountBuilder paymentProviderName(final String paymentProviderName) {
+        this.paymentProviderName = paymentProviderName;
+        return this;
+    }
+
+    public MockAccountBuilder timeZone(final DateTimeZone timeZone) {
+        this.timeZone = timeZone;
+        return this;
+    }
+
+    public MockAccountBuilder locale(final String locale) {
+        this.locale = locale;
+        return this;
+    }
+
+    public MockAccountBuilder address1(final String address1) {
+        this.address1 = address1;
+        return this;
+    }
+
+    public MockAccountBuilder address2(final String address2) {
+        this.address2 = address2;
+        return this;
+    }
+
+    public MockAccountBuilder companyName(final String companyName) {
+        this.companyName = companyName;
+        return this;
+    }
+
+    public MockAccountBuilder city(final String city) {
+        this.city = city;
+        return this;
+    }
+
+    public MockAccountBuilder stateOrProvince(final String stateOrProvince) {
+        this.stateOrProvince = stateOrProvince;
+        return this;
+    }
+
+    public MockAccountBuilder postalCode(final String postalCode) {
+        this.postalCode = postalCode;
+        return this;
+    }
+
+    public MockAccountBuilder country(final String country) {
+        this.country = country;
+        return this;
+    }
+
+    public MockAccountBuilder phone(final String phone) {
+        this.phone = phone;
+        return this;
+    }
+
+    public MockAccountBuilder migrated(final boolean migrated) {
+        this.migrated = migrated;
+        return this;
+    }
+
+    public MockAccountBuilder isNotifiedForInvoices(final boolean isNotifiedForInvoices) {
+        this.isNotifiedForInvoices = isNotifiedForInvoices;
+        return this;
+    }
+
+    public Account build() {
+        return new Account(){
+            
+            @Override
+            public String getExternalKey() {
+                return externalKey;
+            }
+
+            @Override
+            public String getName() {
+               
+                return name;
+            }
+
+            @Override
+            public int getFirstNameLength() {
+               
+                return firstNameLength;
+            }
+
+            @Override
+            public String getEmail() {
+               
+                return email;
+            }
+
+            @Override
+            public int getBillCycleDay() {
+               
+                return billingCycleDay;
+            }
+
+            @Override
+            public Currency getCurrency() {
+               
+                return currency;
+            }
+
+            @Override
+            public String getPaymentProviderName() {
+               
+                return paymentProviderName;
+            }
+
+            @Override
+            public DateTimeZone getTimeZone() {
+               
+                return timeZone;
+            }
+
+            @Override
+            public String getLocale() {
+               
+                return locale;
+            }
+
+            @Override
+            public String getAddress1() {
+               
+                return address1;
+            }
+
+            @Override
+            public String getAddress2() {
+               
+                return address2;
+            }
+
+            @Override
+            public String getCompanyName() {
+               
+                return companyName;
+            }
+
+            @Override
+            public String getCity() {
+               
+                return city;
+            }
+
+            @Override
+            public String getStateOrProvince() {
+               
+                return stateOrProvince;
+            }
+
+            @Override
+            public String getPostalCode() {
+               
+                return postalCode;
+            }
+
+            @Override
+            public String getCountry() {
+               
+                return country;
+            }
+
+            @Override
+            public String getPhone() {
+               
+                return phone;
+            }
+
+            @Override
+            public boolean isMigrated() {
+               
+                return migrated;
+            }
+
+            @Override
+            public boolean isNotifiedForInvoices() {
+               
+                return isNotifiedForInvoices;
+            }
+
+            @Override
+            public String getFieldValue(String fieldName) {
+               
+                return null;
+            }
+
+            @Override
+            public void setFieldValue(String fieldName, String fieldValue) {
+               
+                
+            }
+
+            @Override
+            public void saveFieldValue(String fieldName, String fieldValue, CallContext context) {
+               
+                
+            }
+
+            @Override
+            public List<CustomField> getFieldList() {
+                return null;
+            }
+
+            @Override
+            public void setFields(List<CustomField> fields) {
+            }
+
+            @Override
+            public void saveFields(List<CustomField> fields, CallContext context) {
+            }
+
+            @Override
+            public void clearFields() {
+            }
+
+            @Override
+            public void clearPersistedFields(CallContext context) {
+            }
+
+            @Override
+            public ObjectType getObjectType() {
+                throw new UnsupportedOperationException();
+            }
+
+            @Override
+            public UUID getId() {
+                return id;
+            }
+
+            @Override
+            public List<Tag> getTagList() {
+                return null;
+            }
+
+            @Override
+            public boolean hasTag(TagDefinition tagDefinition) {
+                return false;
+            }
+
+            @Override
+            public boolean hasTag(ControlTagType controlTagType) {
+                return false;
+            }
+
+            @Override
+            public void addTag(TagDefinition definition) {
+            }
+
+            @Override
+            public void addTags(List<Tag> tags) {
+            }
+
+            @Override
+            public void addTagsFromDefinitions(List<TagDefinition> tagDefinitions) {
+            }
+
+            @Override
+            public void clearTags() {
+            }
+
+            @Override
+            public void removeTag(TagDefinition definition) {
+            }
+
+            @Override
+            public boolean generateInvoice() {
+                return true;
+            }
+
+            @Override
+            public boolean processPayment() {
+                return true;
+            }
+
+            @Override
+            public BlockingState getBlockingState() {
+                return null;
+            }
+
+            @Override
+            public MutableAccountData toMutableAccountData() {
+                throw new NotImplementedException();
+            }
+        };
+    }
+}
diff --git a/util/src/test/java/com/ning/billing/util/bus/TestEventBusBase.java b/util/src/test/java/com/ning/billing/util/bus/TestEventBusBase.java
index 371d1f9..ddc89ed 100644
--- a/util/src/test/java/com/ning/billing/util/bus/TestEventBusBase.java
+++ b/util/src/test/java/com/ning/billing/util/bus/TestEventBusBase.java
@@ -49,7 +49,8 @@ public class TestEventBusBase {
         eventBus.stop();
     }
 
-    public static final class MyEvent implements BusEvent {
+    
+    public static  class MyEvent implements BusEvent {
         
         private String name;
         private Long value;
@@ -91,6 +92,18 @@ public class TestEventBusBase {
             return type;
         }
     }
+    
+    public static final class MyEventWithException extends MyEvent {
+        
+        @JsonCreator
+        public MyEventWithException(@JsonProperty("name") String name,
+                @JsonProperty("value") Long value,
+                @JsonProperty("token") UUID token,
+                @JsonProperty("type") String type) {
+            super(name, value, token, type);
+        }        
+    }
+
 
     public static final class MyOtherEvent implements BusEvent {
 
@@ -135,6 +148,12 @@ public class TestEventBusBase {
             return type;
         }
     }
+    
+    public static class MyEventHandlerException extends RuntimeException {
+        public MyEventHandlerException(String msg) {
+            super(msg);
+        }
+    }
 
     public static class MyEventHandler {
 
@@ -158,6 +177,11 @@ public class TestEventBusBase {
             //log.debug("Got event {} {}", event.name, event.value);
         }
 
+        @Subscribe
+        public synchronized void processEvent(MyEventWithException event) {
+            throw new MyEventHandlerException("FAIL");
+        }
+        
         public synchronized boolean waitForCompletion(long timeoutMs) {
 
             long ini = System.currentTimeMillis();
@@ -176,6 +200,20 @@ public class TestEventBusBase {
         }
     }
 
+    public void testSimpleWithException() {
+        try {
+        MyEventHandler handler = new MyEventHandler(1);
+        eventBus.register(handler);
+
+        eventBus.post(new MyEventWithException("my-event", 1L, UUID.randomUUID(), BusEventType.ACCOUNT_CHANGE.toString()));
+        
+        Thread.sleep(50000);
+        } catch (Exception e) {
+            
+        }
+        
+    }
+    
     public void testSimple() {
         try {
 
diff --git a/util/src/test/java/com/ning/billing/util/bus/TestPersistentEventBus.java b/util/src/test/java/com/ning/billing/util/bus/TestPersistentEventBus.java
index 530a4e7..b42b694 100644
--- a/util/src/test/java/com/ning/billing/util/bus/TestPersistentEventBus.java
+++ b/util/src/test/java/com/ning/billing/util/bus/TestPersistentEventBus.java
@@ -82,9 +82,16 @@ public class TestPersistentEventBus extends TestEventBusBase {
         }
     }
     
-    @Test(groups = "slow")
+    @Test(groups = {"slow"})
     public void testSimple() {
         super.testSimple();
     }
+    
+    // Until Guava fixes exception handling, r13?
+    @Test(groups={"slow"}, enabled=false)
+    public void testSimpleWithException() {
+        super.testSimpleWithException();
+        
+    }
  
 }
diff --git a/util/src/test/java/com/ning/billing/util/clock/ClockMock.java b/util/src/test/java/com/ning/billing/util/clock/ClockMock.java
index 9e51a80..ab702cb 100644
--- a/util/src/test/java/com/ning/billing/util/clock/ClockMock.java
+++ b/util/src/test/java/com/ning/billing/util/clock/ClockMock.java
@@ -16,147 +16,134 @@
 
 package com.ning.billing.util.clock;
 
-import com.ning.billing.catalog.api.Duration;
 import org.joda.time.DateTime;
 import org.joda.time.DateTimeZone;
+import org.joda.time.Days;
+import org.joda.time.Months;
+import org.joda.time.MutablePeriod;
+import org.joda.time.Period;
+import org.joda.time.ReadablePeriod;
+import org.joda.time.Weeks;
+import org.joda.time.Years;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import java.util.ArrayList;
-import java.util.List;
-
-// STEPH should really be in tests but not accessible from other sub modules
-public class ClockMock extends DefaultClock {
+import com.ning.billing.catalog.api.Duration;
+import com.ning.billing.catalog.api.TimeUnit;
 
+public class ClockMock implements Clock {
+    
+    private MutablePeriod delta = new MutablePeriod();
     private static final Logger log = LoggerFactory.getLogger(ClockMock.class);
 
-    private enum DeltaType {
-        DELTA_NONE,
-        DELTA_DURATION,
-        DELTA_ABS
-    }
-
-    private long deltaFromRealityMs;
-    private List<Duration> deltaFromRealityDuration;
-    private long deltaFromRealityDurationEpsilon;
-    private DeltaType deltaType;
-
-    public ClockMock() {
-        deltaType = DeltaType.DELTA_NONE;
-        deltaFromRealityMs = 0;
-        deltaFromRealityDurationEpsilon = 0;
-        deltaFromRealityDuration = null;
-    }
-
+     
     @Override
     public synchronized DateTime getNow(DateTimeZone tz) {
-        return adjust(super.getNow(tz));
+        return getUTCNow().toDateTime(tz);
     }
 
     @Override
     public synchronized DateTime getUTCNow() {
-        return getNow(DateTimeZone.UTC);
+        return truncate(adjust(now()));
+    }
+    
+    private DateTime adjust(DateTime now) {
+        return now.plus(delta);
     }
 
-    private void logClockAdjustment(DateTime prev, DateTime next) {
-        log.info(String.format("            ************      ADJUSTING CLOCK FROM %s to %s     ********************", prev, next));
+    public synchronized void setTime(DateTime time) {
+        DateTime prev = getUTCNow();
+        delta = new MutablePeriod(now(), time);
+        logChange(prev);
+    }
+    
+    public synchronized void addDays(int days) {
+        adjustTo(Days.days(days));
+    }
+    
+    public synchronized void addWeeks(int weeks) {
+        adjustTo(Weeks.weeks(weeks));
+    }
+    
+    public synchronized void addMonths(int months) {
+        adjustTo(Months.months(months));
+    }
+    
+    public synchronized void addYears(int years) {
+        adjustTo(Years.years(years));
+    }
+    
+    public synchronized void reset() {
+        delta = new MutablePeriod();
+    }
+    
+    @Override
+    public String toString() {
+        return getUTCNow().toString();
+    }
+    
+    private void adjustTo(ReadablePeriod period) {
+        DateTime prev = getUTCNow();
+        delta.add(period);
+        logChange(prev);
+    }
+    
+    private void logChange(DateTime prev) {     
+        DateTime now = getUTCNow();
+        log.info(String.format("            ************      ADJUSTING CLOCK FROM %s to %s     ********************", prev, now));
+    }
+    
+    private DateTime now() {
+        return new DateTime(DateTimeZone.UTC);
     }
 
-    public synchronized void setDeltaFromReality(Duration delta, long epsilon) {
+    private DateTime truncate(DateTime time) {
+        return time.minus(time.getMillisOfSecond());
+    }
+   
+    //
+    //Backward compatibility stuff
+    //
+    public synchronized void setDeltaFromReality(Duration duration, long epsilon) {
         DateTime prev = getUTCNow();
-        deltaType = DeltaType.DELTA_DURATION;
-        deltaFromRealityDuration = new ArrayList<Duration>();
-        deltaFromRealityDuration.add(delta);
-        deltaFromRealityDurationEpsilon = epsilon;
-        deltaFromRealityMs = 0;
-        logClockAdjustment(prev, getUTCNow());
+        delta.addMillis((int)epsilon);
+        addDeltaFromReality(duration);
+        logChange(prev);
+        
     }
 
     public synchronized void addDeltaFromReality(Duration delta) {
-        DateTime prev = getUTCNow();
-        if (deltaType != DeltaType.DELTA_DURATION) {
-            throw new RuntimeException("ClockMock should be set with type DELTA_DURATION");
-        }
-        deltaFromRealityDuration.add(delta);
-        logClockAdjustment(prev, getUTCNow());
+        adjustTo(periodFromDuration(delta));
     }
 
     public synchronized void setDeltaFromReality(long delta) {
-        DateTime prev = getUTCNow();
-        deltaType = DeltaType.DELTA_ABS;
-        deltaFromRealityDuration = null;
-        deltaFromRealityDurationEpsilon = 0;
-        deltaFromRealityMs = delta;
-        logClockAdjustment(prev, getUTCNow());
+        adjustTo(new Period(delta));
     }
 
     public synchronized void addDeltaFromReality(long delta) {
-        DateTime prev = getUTCNow();
-        if (deltaType != DeltaType.DELTA_ABS) {
-            throw new RuntimeException("ClockMock should be set with type DELTA_ABS");
-        }
-        deltaFromRealityDuration = null;
-        deltaFromRealityDurationEpsilon = 0;
-        deltaFromRealityMs += delta;
-        logClockAdjustment(prev, getUTCNow());
+        adjustTo(new Period(delta));
     }
 
     public synchronized void resetDeltaFromReality() {
-        deltaType = DeltaType.DELTA_NONE;
-        deltaFromRealityDuration = null;
-        deltaFromRealityDurationEpsilon = 0;
-        deltaFromRealityMs = 0;
+        reset();
     }
+    
+    public ReadablePeriod periodFromDuration(Duration duration) {
+        if (duration.getUnit() != TimeUnit.UNLIMITED) {return new Period();}
 
-    private DateTime adjust(DateTime realNow) {
-        switch(deltaType) {
-            case DELTA_NONE:
-                return realNow;
-            case DELTA_ABS:
-                return adjustFromAbsolute(realNow);
-            case DELTA_DURATION:
-                return adjustFromDuration(realNow);
-            default:
-                return null;
-        }
-    }
-
-    private DateTime adjustFromDuration(DateTime input) {
-
-        DateTime result = input;
-        for (Duration cur : deltaFromRealityDuration) {
-            switch (cur.getUnit()) {
+        switch (duration.getUnit()) {
             case DAYS:
-                result = result.plusDays(cur.getNumber());
-                break;
-
+                return Days.days(duration.getNumber());
             case MONTHS:
-                result = result.plusMonths(cur.getNumber());
-                break;
-
+                return Months.months(duration.getNumber());
             case YEARS:
-                result = result.plusYears(cur.getNumber());
-                break;
-
+                return Years.years(duration.getNumber());
             case UNLIMITED:
-            default:
-                throw new RuntimeException("ClockMock is adjusting an unlimited time period");
-            }
+                return Years.years(100);
+           default:
+                return new Period();
         }
-        if (deltaFromRealityDurationEpsilon != 0) {
-            result = result.plus(deltaFromRealityDurationEpsilon);
-        }
-        return result;
-    }
-
-    private DateTime adjustFromAbsolute(DateTime input) {
-        return truncateMs(input.plus(deltaFromRealityMs));
     }
-
-    @Override
-    public String toString() {
-        return getUTCNow().toString();
-    }
-
     
+
 }
diff --git a/util/src/test/java/com/ning/billing/util/clock/MockClockModule.java b/util/src/test/java/com/ning/billing/util/clock/MockClockModule.java
index a8f0605..ece94b0 100644
--- a/util/src/test/java/com/ning/billing/util/clock/MockClockModule.java
+++ b/util/src/test/java/com/ning/billing/util/clock/MockClockModule.java
@@ -28,3 +28,4 @@ public class MockClockModule extends AbstractModule {
 	}
 
 }
+ 
\ No newline at end of file
diff --git a/util/src/test/java/com/ning/billing/util/clock/OldClockMock.java b/util/src/test/java/com/ning/billing/util/clock/OldClockMock.java
new file mode 100644
index 0000000..b31db77
--- /dev/null
+++ b/util/src/test/java/com/ning/billing/util/clock/OldClockMock.java
@@ -0,0 +1,162 @@
+/*
+ * 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.util.clock;
+
+import com.ning.billing.catalog.api.Duration;
+import org.joda.time.DateTime;
+import org.joda.time.DateTimeZone;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.ArrayList;
+import java.util.List;
+
+// STEPH should really be in tests but not accessible from other sub modules
+public class OldClockMock extends DefaultClock {
+
+    private static final Logger log = LoggerFactory.getLogger(OldClockMock.class);
+
+    private enum DeltaType {
+        DELTA_NONE,
+        DELTA_DURATION,
+        DELTA_ABS
+    }
+
+    private long deltaFromRealityMs;
+    private List<Duration> deltaFromRealityDuration;
+    private long deltaFromRealityDurationEpsilon;
+    private DeltaType deltaType;
+
+    public OldClockMock() {
+        deltaType = DeltaType.DELTA_NONE;
+        deltaFromRealityMs = 0;
+        deltaFromRealityDurationEpsilon = 0;
+        deltaFromRealityDuration = null;
+    }
+
+    @Override
+    public synchronized DateTime getNow(DateTimeZone tz) {
+        return adjust(super.getNow(tz));
+    }
+
+    @Override
+    public synchronized DateTime getUTCNow() {
+        return getNow(DateTimeZone.UTC);
+    }
+
+    private void logClockAdjustment(DateTime prev, DateTime next) {
+        log.info(String.format("            ************      ADJUSTING CLOCK FROM %s to %s     ********************", prev, next));
+    }
+
+    public synchronized void setDeltaFromReality(Duration delta, long epsilon) {
+        DateTime prev = getUTCNow();
+        deltaType = DeltaType.DELTA_DURATION;
+        deltaFromRealityDuration = new ArrayList<Duration>();
+        deltaFromRealityDuration.add(delta);
+        deltaFromRealityDurationEpsilon = epsilon;
+        deltaFromRealityMs = 0;
+        logClockAdjustment(prev, getUTCNow());
+    }
+
+    public synchronized void addDeltaFromReality(Duration delta) {
+        DateTime prev = getUTCNow();
+        if (deltaType != DeltaType.DELTA_DURATION) {
+            throw new RuntimeException("ClockMock should be set with type DELTA_DURATION");
+        }
+        deltaFromRealityDuration.add(delta);
+        logClockAdjustment(prev, getUTCNow());
+    }
+
+    public synchronized void setDeltaFromReality(long delta) {
+        DateTime prev = getUTCNow();
+        deltaType = DeltaType.DELTA_ABS;
+        deltaFromRealityDuration = null;
+        deltaFromRealityDurationEpsilon = 0;
+        deltaFromRealityMs = delta;
+        logClockAdjustment(prev, getUTCNow());
+    }
+
+    public synchronized void addDeltaFromReality(long delta) {
+        DateTime prev = getUTCNow();
+        if (deltaType != DeltaType.DELTA_ABS) {
+            throw new RuntimeException("ClockMock should be set with type DELTA_ABS");
+        }
+        deltaFromRealityDuration = null;
+        deltaFromRealityDurationEpsilon = 0;
+        deltaFromRealityMs += delta;
+        logClockAdjustment(prev, getUTCNow());
+    }
+
+    public synchronized void resetDeltaFromReality() {
+        deltaType = DeltaType.DELTA_NONE;
+        deltaFromRealityDuration = null;
+        deltaFromRealityDurationEpsilon = 0;
+        deltaFromRealityMs = 0;
+    }
+
+    private DateTime adjust(DateTime realNow) {
+        switch(deltaType) {
+            case DELTA_NONE:
+                return realNow;
+            case DELTA_ABS:
+                return adjustFromAbsolute(realNow);
+            case DELTA_DURATION:
+                return adjustFromDuration(realNow);
+            default:
+                return null;
+        }
+    }
+
+    private DateTime adjustFromDuration(DateTime input) {
+
+        DateTime result = input;
+        for (Duration cur : deltaFromRealityDuration) {
+            switch (cur.getUnit()) {
+            case DAYS:
+                result = result.plusDays(cur.getNumber());
+                break;
+
+            case MONTHS:
+                result = result.plusMonths(cur.getNumber());
+                break;
+
+            case YEARS:
+                result = result.plusYears(cur.getNumber());
+                break;
+
+            case UNLIMITED:
+            default:
+                throw new RuntimeException("ClockMock is adjusting an unlimited time period");
+            }
+        }
+        if (deltaFromRealityDurationEpsilon != 0) {
+            result = result.plus(deltaFromRealityDurationEpsilon);
+        }
+        return result;
+    }
+
+    private DateTime adjustFromAbsolute(DateTime input) {
+        return truncateMs(input.plus(deltaFromRealityMs));
+    }
+
+    @Override
+    public String toString() {
+        return getUTCNow().toString();
+    }
+
+    
+}
diff --git a/util/src/test/java/com/ning/billing/util/notificationq/dao/TestNotificationSqlDao.java b/util/src/test/java/com/ning/billing/util/notificationq/dao/TestNotificationSqlDao.java
index cc7c458..6cf0eb9 100644
--- a/util/src/test/java/com/ning/billing/util/notificationq/dao/TestNotificationSqlDao.java
+++ b/util/src/test/java/com/ning/billing/util/notificationq/dao/TestNotificationSqlDao.java
@@ -20,11 +20,10 @@ import java.io.IOException;
 import java.sql.SQLException;
 import java.util.List;
 import java.util.UUID;
-import java.util.concurrent.atomic.AtomicInteger;
+
 import org.apache.commons.io.IOUtils;
 import org.joda.time.DateTime;
 import org.joda.time.DateTimeZone;
-import org.skife.jdbi.v2.DBI;
 import org.skife.jdbi.v2.Handle;
 import org.skife.jdbi.v2.IDBI;
 import org.skife.jdbi.v2.tweak.HandleCallback;
@@ -49,8 +48,8 @@ import static org.testng.Assert.assertNotNull;
 @Guice(modules = TestNotificationSqlDao.TestNotificationSqlDaoModule.class)
 public class TestNotificationSqlDao {
 
-    private static AtomicInteger sequenceId = new AtomicInteger();
-
+    private final static String hostname = "Yop";
+    
     @Inject
     private IDBI dbi;
 
@@ -104,12 +103,12 @@ public class TestNotificationSqlDao {
 
         String notificationKey = UUID.randomUUID().toString();
         DateTime effDt = new DateTime();
-        Notification notif = new DefaultNotification("testBasic",notificationKey, effDt);
+        Notification notif = new DefaultNotification("testBasic", hostname, notificationKey, effDt);
         dao.insertNotification(notif);
 
         Thread.sleep(1000);
         DateTime now = new DateTime();
-        List<Notification> notifications = dao.getReadyNotifications(now.toDate(), 3, "testBasic");
+        List<Notification> notifications = dao.getReadyNotifications(now.toDate(), hostname, 3, "testBasic");
         assertNotNull(notifications);
         assertEquals(notifications.size(), 1);
 
@@ -121,20 +120,20 @@ public class TestNotificationSqlDao {
         assertEquals(notification.getNextAvailableDate(), null);
 
         DateTime nextAvailable = now.plusMinutes(5);
-        int res = dao.claimNotification(ownerId, nextAvailable.toDate(), notification.getId(), now.toDate());
+        int res = dao.claimNotification(ownerId, nextAvailable.toDate(), notification.getId().toString(), now.toDate());
         assertEquals(res, 1);
-        dao.insertClaimedHistory(sequenceId.incrementAndGet(), ownerId, now.toDate(), notification.getUUID().toString());
+        dao.insertClaimedHistory(ownerId, now.toDate(), notification.getId().toString());
 
-        notification = fetchNotification(notification.getUUID().toString());
+        notification = fetchNotification(notification.getId().toString());
         assertEquals(notification.getNotificationKey(), notificationKey);
         validateDate(notification.getEffectiveDate(), effDt);
         assertEquals(notification.getOwner().toString(), ownerId);
         assertEquals(notification.getProcessingState(), NotificationLifecycleState.IN_PROCESSING);
         validateDate(notification.getNextAvailableDate(), nextAvailable);
 
-        dao.clearNotification(notification.getId(), ownerId);
+        dao.clearNotification(notification.getId().toString(), ownerId);
 
-        notification = fetchNotification(notification.getUUID().toString());
+        notification = fetchNotification(notification.getId().toString());
         assertEquals(notification.getNotificationKey(), notificationKey);
         validateDate(notification.getEffectiveDate(), effDt);
         //assertEquals(notification.getOwner(), null);
@@ -153,6 +152,7 @@ public class TestNotificationSqlDao {
                 		", id" +
                 		", notification_key" +
                 		", created_date" +
+                		", creating_owner" +
                 		", effective_date" +
                 		", queue_name" +
                 		", processing_owner" +
@@ -197,12 +197,6 @@ public class TestNotificationSqlDao {
             bind(MysqlTestingHelper.class).toInstance(helper);
             IDBI dbi = helper.getDBI();
             bind(IDBI.class).toInstance(dbi);
-
-            /*
-            bind(DBI.class).toProvider(DBIProvider.class).asEagerSingleton();
-            final DbiConfig config = new ConfigurationObjectFactory(System.getProperties()).build(DbiConfig.class);
-            bind(DbiConfig.class).toInstance(config);
-            */
         }
     }
 }
diff --git a/util/src/test/java/com/ning/billing/util/notificationq/MockNotificationQueue.java b/util/src/test/java/com/ning/billing/util/notificationq/MockNotificationQueue.java
index a9cd1db..7d8b477 100644
--- a/util/src/test/java/com/ning/billing/util/notificationq/MockNotificationQueue.java
+++ b/util/src/test/java/com/ning/billing/util/notificationq/MockNotificationQueue.java
@@ -50,7 +50,7 @@ public class MockNotificationQueue extends NotificationQueueBase implements Noti
 
     @Override
     public void recordFutureNotification(DateTime futureNotificationTime, NotificationKey notificationKey) {
-        Notification notification = new DefaultNotification("MockQueue", notificationKey.toString(), futureNotificationTime);
+        Notification notification = new DefaultNotification("MockQueue", hostname, notificationKey.toString(), futureNotificationTime);
         synchronized(notifications) {
             notifications.add(notification);
         }
@@ -75,7 +75,7 @@ public class MockNotificationQueue extends NotificationQueueBase implements Noti
     }
 
     @Override
-    protected int doProcessEvents(int sequenceId) {
+    public int doProcessEvents() {
 
         int result = 0;
 
@@ -96,7 +96,7 @@ public class MockNotificationQueue extends NotificationQueueBase implements Noti
         result = readyNotifications.size();
         for (Notification cur : readyNotifications) {
             handler.handleReadyNotification(cur.getNotificationKey(), cur.getEffectiveDate());
-            DefaultNotification processedNotification = new DefaultNotification(-1L, cur.getUUID(), hostname, "MockQueue", clock.getUTCNow().plus(config.getDaoClaimTimeMs()), NotificationLifecycleState.PROCESSED, cur.getNotificationKey(), cur.getEffectiveDate());
+            DefaultNotification processedNotification = new DefaultNotification(-1L, cur.getId(), hostname, hostname, "MockQueue", clock.getUTCNow().plus(config.getDaoClaimTimeMs()), NotificationLifecycleState.PROCESSED, cur.getNotificationKey(), cur.getEffectiveDate());
             oldNotifications.add(cur);
             processedNotifications.add(processedNotification);
         }