killbill-memoizeit

Various fixes to overdue to get a beatrix test working end-to-end.

5/12/2012 3:24:53 PM

Changes

overdue/src/main/java/com/ning/billing/overdue/glue/OverdueModule.java 34(+0 -34)

Details

diff --git a/api/src/main/java/com/ning/billing/overdue/config/api/OverdueStateSet.java b/api/src/main/java/com/ning/billing/overdue/config/api/OverdueStateSet.java
index 693fa8f..2c043d6 100644
--- a/api/src/main/java/com/ning/billing/overdue/config/api/OverdueStateSet.java
+++ b/api/src/main/java/com/ning/billing/overdue/config/api/OverdueStateSet.java
@@ -24,7 +24,7 @@ import com.ning.billing.overdue.OverdueState;
 
 public interface OverdueStateSet<T extends Blockable> {
 
-    public abstract OverdueState<T> findClearState() throws OverdueApiException;
+    public abstract OverdueState<T> getClearState() throws OverdueApiException;
 
     public abstract OverdueState<T> findState(String stateName) throws OverdueApiException;
 
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 3c937a7..0eaae76 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
@@ -30,6 +30,7 @@ import com.google.inject.AbstractModule;
 import com.google.inject.Inject;
 import com.google.inject.Injector;
 import com.ning.billing.account.glue.AccountModule;
+import com.ning.billing.beatrix.integration.overdue.IntegrationTestOverdueModule;
 import com.ning.billing.beatrix.lifecycle.DefaultLifecycle;
 import com.ning.billing.beatrix.lifecycle.Lifecycle;
 import com.ning.billing.catalog.api.CatalogService;
@@ -44,6 +45,7 @@ import com.ning.billing.invoice.api.InvoiceService;
 import com.ning.billing.invoice.glue.DefaultInvoiceModule;
 import com.ning.billing.junction.glue.DefaultJunctionModule;
 import com.ning.billing.lifecycle.KillbillService;
+import com.ning.billing.overdue.OverdueService;
 import com.ning.billing.payment.api.PaymentService;
 import com.ning.billing.payment.provider.MockPaymentProviderPluginModule;
 import com.ning.billing.payment.setup.PaymentModule;
@@ -99,6 +101,7 @@ public class MockModule extends AbstractModule {
         install(new TemplateModule());
         install(new PaymentMockModule());
         install(new DefaultJunctionModule());
+        install(new IntegrationTestOverdueModule());
     }
 
     private static final class PaymentMockModule extends PaymentModule {
@@ -133,6 +136,7 @@ public class MockModule extends AbstractModule {
                             .add(injector.getInstance(EntitlementService.class))
                             .add(injector.getInstance(InvoiceService.class))
                             .add(injector.getInstance(PaymentService.class))
+                            .add(injector.getInstance(OverdueService.class))
                             .build();
             return services;
         }
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
index edc9ec1..0242954 100644
--- 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
@@ -16,10 +16,17 @@
 
 package com.ning.billing.beatrix.integration.overdue;
 
+import static com.jayway.awaitility.Awaitility.await;
+import static java.util.concurrent.TimeUnit.SECONDS;
 import static org.testng.Assert.assertNotNull;
+import static org.testng.Assert.assertTrue;
 
 import java.io.ByteArrayInputStream;
 import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.concurrent.Callable;
 
 import org.joda.time.DateTime;
 import org.testng.Assert;
@@ -29,7 +36,9 @@ import org.testng.annotations.Guice;
 import org.testng.annotations.Test;
 
 import com.google.inject.Inject;
+import com.google.inject.name.Named;
 import com.ning.billing.account.api.Account;
+import com.ning.billing.api.TestApiListener.NextEvent;
 import com.ning.billing.beatrix.integration.MockModule;
 import com.ning.billing.beatrix.integration.TestIntegrationBase;
 import com.ning.billing.catalog.api.BillingPeriod;
@@ -38,10 +47,18 @@ 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.invoice.api.Invoice;
+import com.ning.billing.invoice.api.InvoiceUserApi;
 import com.ning.billing.junction.api.BlockingApi;
-import com.ning.billing.junction.api.BlockingState;
+import com.ning.billing.overdue.OverdueUserApi;
 import com.ning.billing.overdue.config.OverdueConfig;
+import com.ning.billing.overdue.wrapper.MockOverdueWrapperFactory;
+import com.ning.billing.overdue.wrapper.OverdueWrapperFactory;
+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.provider.MockPaymentProviderPlugin;
+import com.ning.billing.util.callcontext.DefaultCallContext;
 import com.ning.billing.util.clock.ClockMock;
 import com.ning.billing.util.config.XMLLoader;
 
@@ -51,50 +68,83 @@ public class TestOverdueIntegration extends TestIntegrationBase {
     private final String configXml =  
             "<overdueConfig>" +
                     "   <bundleOverdueStates>" +
-                    "       <state name=\"OD1\">" +
+                    "       <state name=\"OD3\">" +
                     "           <condition>" +
                     "               <timeSinceEarliestUnpaidInvoiceEqualsOrExceeds>" +
-                    "                   <unit>MONTHS</unit><number>1</number>" +
+                    "                   <unit>DAYS</unit><number>50</number>" +
                     "               </timeSinceEarliestUnpaidInvoiceEqualsOrExceeds>" +
                     "           </condition>" +
-                    "           <externalMessage>Reached OD1</externalMessage>" +
+                    "           <externalMessage>Reached OD3</externalMessage>" +
                     "           <blockChanges>true</blockChanges>" +
-                    "           <disableEntitlementAndChangesBlocked>false</disableEntitlementAndChangesBlocked>" +
+                    "           <disableEntitlementAndChangesBlocked>true</disableEntitlementAndChangesBlocked>" +
+                    "           <autoReevaluationInterval>" +
+                    "               <unit>DAYS</unit><number>5</number>" +
+                    "           </autoReevaluationInterval>" +
                     "       </state>" +
                     "       <state name=\"OD2\">" +
                     "           <condition>" +
                     "               <timeSinceEarliestUnpaidInvoiceEqualsOrExceeds>" +
-                    "                   <unit>MONTHS</unit><number>2</number>" +
+                    "                   <unit>DAYS</unit><number>40</number>" +
                     "               </timeSinceEarliestUnpaidInvoiceEqualsOrExceeds>" +
                     "           </condition>" +
-                    "           <externalMessage>Reached OD1</externalMessage>" +
+                    "           <externalMessage>Reached OD2</externalMessage>" +
                     "           <blockChanges>true</blockChanges>" +
                     "           <disableEntitlementAndChangesBlocked>true</disableEntitlementAndChangesBlocked>" +
+                    "           <autoReevaluationInterval>" +
+                    "               <unit>DAYS</unit><number>5</number>" +
+                    "           </autoReevaluationInterval>" +
+                    "       </state>" +
+                    "       <state name=\"OD1\">" +
+                    "           <condition>" +
+                    "               <timeSinceEarliestUnpaidInvoiceEqualsOrExceeds>" +
+                    "                   <unit>DAYS</unit><number>30</number>" +
+                    "               </timeSinceEarliestUnpaidInvoiceEqualsOrExceeds>" +
+                    "           </condition>" +
+                    "           <externalMessage>Reached OD1</externalMessage>" +
+                    "           <blockChanges>true</blockChanges>" +
+                    "           <disableEntitlementAndChangesBlocked>false</disableEntitlementAndChangesBlocked>" +
+                    "           <autoReevaluationInterval>" +
+                    "               <unit>DAYS</unit><number>100</number>" + // this number is intentionally too high
+                    "           </autoReevaluationInterval>" +
                     "       </state>" +
                     "   </bundleOverdueStates>" +
                     "</overdueConfig>";
     private OverdueConfig config; 
-    
+
     @Inject
     private ClockMock clock;
+
+    @Inject
+    private @Named("yoyo") MockPaymentProviderPlugin paymentPlugin;
+
+    @Inject
+    private BlockingApi blockingApi;
     
     @Inject
-    private MockPaymentProviderPlugin paymentPlugin;
+    private OverdueWrapperFactory overdueWrapperFactory;
     
     @Inject
-    private BlockingApi blockingApi;
+    private OverdueUserApi overdueApi;
+    
+    @Inject
+    private PaymentApi paymentApi;
     
+    @Inject
+    private InvoiceUserApi invoiceApi;
+
     private Account account;
     private SubscriptionBundle bundle;
     private String productName;
     private BillingPeriod term;
     private String planSetName;
-    
+
     @BeforeMethod(groups = {"slow"})
     public void setupOverdue() throws Exception {
         InputStream is = new ByteArrayInputStream(configXml.getBytes());
         config = XMLLoader.getObjectFromStreamNoValidation(is,  OverdueConfig.class);
-        Account account = accountUserApi.createAccount(getAccountData(25), null, null, context);
+        ((MockOverdueWrapperFactory)overdueWrapperFactory).setOverdueConfig(config);
+        
+        account = accountUserApi.createAccount(getAccountData(25), null, null, context);
         assertNotNull(account);
 
         bundle = entitlementUserApi.createBundleForAccount(account.getId(), "whatever", context);
@@ -102,41 +152,106 @@ public class TestOverdueIntegration extends TestIntegrationBase {
         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
     }
-    
+
     @Test(groups={"slow"}, enabled = true)
     public void testBasicOverdueState() throws Exception {
         clock.setTime(new DateTime(2012, 5, 1, 0, 3, 42, 0));
-               
+        paymentPlugin.makeAllInvoicesFail(true);
+        
         // set next invoice to fail and create network 
-        paymentPlugin.makeNextInvoiceFail();
+        busHandler.pushExpectedEvents(NextEvent.CREATE, NextEvent.INVOICE);
         SubscriptionData baseSubscription = subscriptionDataFromSubscription(entitlementUserApi.createSubscription(bundle.getId(),
                 new PlanPhaseSpecifier(productName, ProductCategory.BASE, term, planSetName, null), null, context));
         assertNotNull(baseSubscription);
+        assertTrue(busHandler.isCompleted(DELAY));
+
+        busHandler.pushExpectedEvents(NextEvent.PHASE, NextEvent.INVOICE, NextEvent.PAYMENT_ERROR);
+        clock.addDays(30); // DAY 30 have to get out of trial before first payment
+        assertTrue(busHandler.isCompleted(DELAY));
+        
+        // should still be in clear state
+        checkODState(BlockingApi.CLEAR_STATE_NAME);
+    
+        clock.addDays(15); // DAY 45 - 15 days after invoice
+        assertTrue(busHandler.isCompleted(DELAY));
+        overdueApi.refreshOverdueStateFor(bundle); // trigger a refresh because there are no events to do it for us
+        //should still be in clear state
+        checkODState(BlockingApi.CLEAR_STATE_NAME);
+
+        busHandler.pushExpectedEvents(NextEvent.INVOICE, NextEvent.PAYMENT_ERROR);
+        clock.addDays(20); // DAY 65 - 35 days after invoice
+        assertTrue(busHandler.isCompleted(DELAY));
+        
+        //Now we should be in OD1
+        checkODState("OD1");
+          
+        clock.addDays(2); //DAY 67 - 37 days after invoice
+        assertTrue(busHandler.isCompleted(DELAY));
+        overdueApi.refreshOverdueStateFor(bundle); // trigger a refresh because there are no events to do it for us
+        // should still be in OD1
+        checkODState("OD1");
 
-       // advance time 2weeks
-       clock.addWeeks(2);
+        //busHandler.pushExpectedEvents(NextEvent.INVOICE, NextEvent.PAYMENT_ERROR);
+        clock.addDays(8); //DAY 75 - 45 days after invoice
+        assertTrue(busHandler.isCompleted(DELAY));
+        overdueApi.refreshOverdueStateFor(bundle); // trigger a refresh because there are no events to do it for us
+        // should still be in OD1
+        checkODState("OD2");
         
-       // should still be in clear state
-       BlockingState state = blockingApi.getBlockingStateFor(bundle);
-       Assert.assertEquals(state.getStateName(), BlockingApi.CLEAR_STATE_NAME);
-       // set next invoice to fail and advance time 1 month
-       clock.addWeeks(4);
+        busHandler.pushExpectedEvents(NextEvent.INVOICE, NextEvent.PAYMENT_ERROR);
+        clock.addDays(10); //DAY 85 - 55 days after invoice
+        assertTrue(busHandler.isCompleted(DELAY));
+        // should now be in OD2 state once the update is processed
+        checkODState("OD3");
+        
+        paymentPlugin.makeAllInvoicesFail(false);
+        Collection<Invoice> invoices = invoiceApi.getUnpaidInvoicesByAccountId(account.getId(), clock.getUTCNow());
+        List<String> invoiceIds = new ArrayList<String>();
+        List<PaymentAttempt> paymentAttempts = new ArrayList<PaymentAttempt>();
+        for(Invoice invoice : invoices) {
+//            paymentAttempts.addAll(paymentApi.getPaymentAttemptsForInvoiceId(invoice.getId().toString()));
+//            for(PaymentAttempt pa : paymentAttempts) {
+//                busHandler.pushExpectedEvent(NextEvent.PAYMENT);
+//                try {
+//                paymentApi.createPaymentForPaymentAttempt(pa.getPaymentAttemptId(), new DefaultCallContext("test", null, null, clock));
+//                } catch(PaymentApiException e) {
+//                    log.info("",e);
+//                }
+//            }
+            invoiceIds.add(invoice.getId().toString());
+            
+        }
+        paymentApi.createPayment(account, invoiceIds, new DefaultCallContext("test", null, null, clock));
        
-       // should now be in OD1 state
-       // set next invoice to fail and advance time 1 month
-       // should now be in OD2 state
-       clock.addWeeks(4);
+        assertTrue(busHandler.isCompleted(DELAY));
+        
+        overdueApi.refreshOverdueStateFor(bundle);
+        checkODState(BlockingApi.CLEAR_STATE_NAME);
+
+    }
+
+    private void checkODState(final String expected) {
         
+        try {
+            await().atMost(10, SECONDS).until(new Callable<Boolean>() {
+                @Override
+                public Boolean call() throws Exception {
+                    return expected.equals(blockingApi.getBlockingStateFor(bundle).getStateName()) ;
+                }
+            });
+        } catch (Exception e) {
+           Assert.assertEquals(blockingApi.getBlockingStateFor(bundle).getStateName(), expected);
+        }
     }
 }
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 7996141..c936a52 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
@@ -127,7 +127,7 @@ public class TestIntegration extends TestIntegrationBase {
         busHandler.pushExpectedEvent(NextEvent.CREATE);
         busHandler.pushExpectedEvent(NextEvent.INVOICE);
         busHandler.pushExpectedEvent(NextEvent.PAYMENT);
-        SubscriptionData aoSubscription = subscriptionDataFromSubscription(entitlementUserApi.createSubscription(bundle.getId(),
+        subscriptionDataFromSubscription(entitlementUserApi.createSubscription(bundle.getId(),
                 new PlanPhaseSpecifier("Telescopic-Scope", ProductCategory.ADD_ON, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null), null, context));
         assertTrue(busHandler.isCompleted(DELAY));
         
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..27f8dff 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
@@ -94,6 +94,7 @@ getUnpaidInvoicesByAccountId() ::= <<
   WHERE i.account_id = :accountId AND NOT (i.target_date > :upToDate) AND migrated = 'FALSE'
   GROUP BY i.id, i.account_id, i.invoice_date, i.target_date, i.currency
   HAVING (SUM(iis.amount_invoiced) > SUM(ips.total_paid)) OR (SUM(ips.total_paid) IS NULL)
+  AND SUM(iis.amount_invoiced) > 0
   ORDER BY i.target_date ASC;
 >>
 
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 83487ea..e71a2fd 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
@@ -17,11 +17,12 @@
 package com.ning.billing.overdue.api;
 
 import org.apache.commons.lang.NotImplementedException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 import com.google.inject.Inject;
 import com.ning.billing.ErrorCode;
 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;
@@ -32,22 +33,21 @@ import com.ning.billing.overdue.config.OverdueConfig;
 import com.ning.billing.overdue.config.api.BillingState;
 import com.ning.billing.overdue.config.api.OverdueError;
 import com.ning.billing.overdue.config.api.OverdueStateSet;
-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 { 
-
+    Logger log = LoggerFactory.getLogger(DefaultOverdueUserApi.class);
     
     private final OverdueWrapperFactory factory;
     private final BlockingApi accessApi; 
     private final OverdueConfig overdueConfig;
    
     @Inject
-    public DefaultOverdueUserApi(OverdueWrapperFactory factory,BlockingApi accessApi, ExtendedOverdueService service,  CatalogService catalogService) {
+    public DefaultOverdueUserApi(OverdueWrapperFactory factory,BlockingApi accessApi, OverdueConfig config,  CatalogService catalogService) {
         this.factory = factory;
         this.accessApi = accessApi;
-        this.overdueConfig = service.getOverdueConfig();
+        this.overdueConfig = config;
     }
     
     @SuppressWarnings("unchecked")
@@ -63,8 +63,9 @@ public class DefaultOverdueUserApi implements OverdueUserApi {
     }
     
     @Override
-    public <T extends Blockable> OverdueState<T> refreshOverdueStateFor(T overdueable) throws OverdueError, OverdueApiException {
-        OverdueWrapper<T> wrapper = factory.createOverdueWrapperFor(overdueable);
+    public <T extends Blockable> OverdueState<T> refreshOverdueStateFor(T blockable) throws OverdueError, OverdueApiException {
+        log.info(String.format("Refresh of %s requested", blockable.getId()));
+        OverdueWrapper<T> wrapper = factory.createOverdueWrapperFor(blockable);
         return wrapper.refresh();
     } 
  
diff --git a/overdue/src/main/java/com/ning/billing/overdue/applicator/OverdueStateApplicator.java b/overdue/src/main/java/com/ning/billing/overdue/applicator/OverdueStateApplicator.java
index abd4e0a..139357b 100644
--- a/overdue/src/main/java/com/ning/billing/overdue/applicator/OverdueStateApplicator.java
+++ b/overdue/src/main/java/com/ning/billing/overdue/applicator/OverdueStateApplicator.java
@@ -46,8 +46,8 @@ public class OverdueStateApplicator<T extends Blockable>{
         this.poster = poster;
     }
 
-    public void apply(T overdueable, OverdueState<T> previousOverdueState, OverdueState<T> nextOverdueState) throws OverdueError {
-        if(previousOverdueState.getName().equals(nextOverdueState.getName())) {
+    public void apply(T overdueable, String previousOverdueStateName, OverdueState<T> nextOverdueState) throws OverdueError {
+        if(previousOverdueStateName.equals(nextOverdueState.getName())) {
             return; // nothing to do
         }
 
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 d47f3f3..ccabe19 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
@@ -20,6 +20,7 @@ import java.math.BigDecimal;
 import java.util.Collection;
 import java.util.Comparator;
 import java.util.Iterator;
+import java.util.NoSuchElementException;
 import java.util.SortedSet;
 import java.util.TreeSet;
 import java.util.UUID;
@@ -60,7 +61,11 @@ public abstract class BillingStateCalculator<T extends Blockable> {
     public abstract BillingState<T> calculateBillingState(T overdueable) throws EntitlementUserApiException;
     
     protected DateTime earliest(SortedSet<Invoice> unpaidInvoices) {
-        return unpaidInvoices.first().getInvoiceDate();
+        try {
+            return unpaidInvoices.first().getInvoiceDate();
+        } catch (NoSuchElementException e) {
+            return null;
+        }
     }
 
     protected BigDecimal sumBalance(SortedSet<Invoice> unpaidInvoices) {
diff --git a/overdue/src/main/java/com/ning/billing/overdue/config/Condition.java b/overdue/src/main/java/com/ning/billing/overdue/config/Condition.java
index 6fc8564..490c5f0 100644
--- a/overdue/src/main/java/com/ning/billing/overdue/config/Condition.java
+++ b/overdue/src/main/java/com/ning/billing/overdue/config/Condition.java
@@ -25,6 +25,6 @@ import com.ning.billing.overdue.config.api.BillingState;
 
 public interface Condition<T extends Blockable> {
 
-    public boolean evaluate(BillingState state, DateTime now);
+    public boolean evaluate(BillingState<T> state, DateTime now);
 
 }
\ No newline at end of file
diff --git a/overdue/src/main/java/com/ning/billing/overdue/config/DefaultCondition.java b/overdue/src/main/java/com/ning/billing/overdue/config/DefaultCondition.java
index 44f6c99..36343f8 100644
--- a/overdue/src/main/java/com/ning/billing/overdue/config/DefaultCondition.java
+++ b/overdue/src/main/java/com/ning/billing/overdue/config/DefaultCondition.java
@@ -59,11 +59,17 @@ public class DefaultCondition<T extends Blockable> extends ValidatingConfig<Over
      * @see com.ning.billing.catalog.overdue.Condition#evaluate(com.ning.billing.catalog.api.overdue.BillingState, org.joda.time.DateTime)
      */
 	@Override
-    public boolean evaluate(BillingState state, DateTime now) {
+    public boolean evaluate(BillingState<T> state, DateTime now) {
+	    DateTime unpaidInvoiceTriggerDate = null;
+	    if( timeSinceEarliestUnpaidInvoiceEqualsOrExceeds != null && state.getDateOfEarliestUnpaidInvoice() != null) {  // no date => no unpaid invoices
+	        unpaidInvoiceTriggerDate = state.getDateOfEarliestUnpaidInvoice().plus(timeSinceEarliestUnpaidInvoiceEqualsOrExceeds.toJodaPeriod());
+	    }
+        
 		return 
 				(numberOfUnpaidInvoicesEqualsOrExceeds == null || state.getNumberOfUnpaidInvoices() >= numberOfUnpaidInvoicesEqualsOrExceeds.intValue() ) &&
 				(totalUnpaidInvoiceBalanceEqualsOrExceeds == null || totalUnpaidInvoiceBalanceEqualsOrExceeds.compareTo(state.getBalanceOfUnpaidInvoices()) <= 0) &&
-				(timeSinceEarliestUnpaidInvoiceEqualsOrExceeds == null || !timeSinceEarliestUnpaidInvoiceEqualsOrExceeds.addToDateTime(state.getDateOfEarliestUnpaidInvoice()).isAfter(now)) &&
+				(unpaidInvoiceTriggerDate == null ||
+				 (state.getDateOfEarliestUnpaidInvoice() != null && !unpaidInvoiceTriggerDate.isAfter(now))) &&
 				(responseForLastFailedPayment == null || responseIsIn(state.getResponseForLastFailedPayment(), responseForLastFailedPayment)) &&
 				(controlTag == null || isTagIn(controlTag, state.getTags()));
 	}
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 690d717..4206e45 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
@@ -121,6 +121,10 @@ public class DefaultOverdueState<T extends Blockable> extends ValidatingConfig<O
 		return this;
 	}
 
+	protected DefaultOverdueState<T> setClearState(boolean isClearState) {
+        this.isClearState = isClearState;
+        return this;
+    }
 	protected DefaultOverdueState<T> setExternalMessage(String externalMessage) {
 		this.externalMessage = externalMessage;
 		return this;
diff --git a/overdue/src/main/java/com/ning/billing/overdue/config/DefaultOverdueStateSet.java b/overdue/src/main/java/com/ning/billing/overdue/config/DefaultOverdueStateSet.java
index 277fb1b..59a82bf 100644
--- a/overdue/src/main/java/com/ning/billing/overdue/config/DefaultOverdueStateSet.java
+++ b/overdue/src/main/java/com/ning/billing/overdue/config/DefaultOverdueStateSet.java
@@ -26,6 +26,8 @@ import org.joda.time.Period;
 import com.ning.billing.ErrorCode;
 import com.ning.billing.catalog.api.Duration;
 import com.ning.billing.junction.api.Blockable;
+import com.ning.billing.junction.api.BlockingApi;
+import com.ning.billing.junction.api.BlockingState;
 import com.ning.billing.overdue.OverdueApiException;
 import com.ning.billing.overdue.OverdueState;
 import com.ning.billing.overdue.config.api.BillingState;
@@ -36,21 +38,15 @@ import com.ning.billing.util.config.ValidationErrors;
 @XmlAccessorType(XmlAccessType.NONE)
 public abstract class DefaultOverdueStateSet<T extends Blockable> extends ValidatingConfig<OverdueConfig> implements OverdueStateSet<T> {
     private static final Period ZERO_PERIOD = new Period();
-    private DefaultOverdueState<T> clearState;
+    private DefaultOverdueState<T> clearState = new DefaultOverdueState<T>().setName(BlockingApi.CLEAR_STATE_NAME).setClearState(true);
 
     protected abstract DefaultOverdueState<T>[] getStates();
 
-    private DefaultOverdueState<T> getClearState() throws OverdueApiException {
-        for(DefaultOverdueState<T> overdueState : getStates()) {
-            if(overdueState.isClearState()) {   
-                return overdueState;
-            }
-        }
-        throw new OverdueApiException(ErrorCode.CAT_MISSING_CLEAR_STATE);
-    }
-
     @Override
     public OverdueState<T> findState(String stateName) throws OverdueApiException {
+        if(stateName.equals( BlockingApi.CLEAR_STATE_NAME)) {
+            return clearState;
+        }
         for(DefaultOverdueState<T> state: getStates()) {
             if(state.getName().equals(stateName) ) { return state; }
         }
@@ -62,11 +58,8 @@ public abstract class DefaultOverdueStateSet<T extends Blockable> extends Valida
      * @see com.ning.billing.catalog.overdue.OverdueBillingState#findClearState()
      */
     @Override
-    public DefaultOverdueState<T> findClearState() throws OverdueApiException {
-        if (clearState != null) {
-            clearState = getClearState();
-        }
-        return clearState;
+    public DefaultOverdueState<T> getClearState() throws OverdueApiException {
+       return clearState;
     }
 
     /* (non-Javadoc)
@@ -79,7 +72,7 @@ public abstract class DefaultOverdueStateSet<T extends Blockable> extends Valida
                 return overdueState;
             }
         }
-        return  findClearState();
+        return  getClearState();
     }
 
     @Override
diff --git a/overdue/src/main/java/com/ning/billing/overdue/listener/OverdueDispatcher.java b/overdue/src/main/java/com/ning/billing/overdue/listener/OverdueDispatcher.java
index 8f2d03f..f7b247e 100644
--- a/overdue/src/main/java/com/ning/billing/overdue/listener/OverdueDispatcher.java
+++ b/overdue/src/main/java/com/ning/billing/overdue/listener/OverdueDispatcher.java
@@ -16,15 +16,13 @@
 
 package com.ning.billing.overdue.listener;
 
+import java.util.List;
 import java.util.UUID;
 
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 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.entitlement.api.user.EntitlementUserApi;
 import com.ning.billing.entitlement.api.user.EntitlementUserApiException;
 import com.ning.billing.entitlement.api.user.SubscriptionBundle;
@@ -36,29 +34,25 @@ import com.ning.billing.overdue.wrapper.OverdueWrapperFactory;
 
 public class OverdueDispatcher {
     Logger log = LoggerFactory.getLogger(OverdueDispatcher.class);
-    
+
     private final EntitlementUserApi entitlementUserApi;
-    private final AccountUserApi accountUserApi;
     private final OverdueWrapperFactory factory;
-    
+
     @Inject
-    public OverdueDispatcher(AccountUserApi accountUserApi, 
+    public OverdueDispatcher(
             EntitlementUserApi entitlementUserApi, 
             OverdueWrapperFactory factory) {
-        this.accountUserApi = accountUserApi;
         this.entitlementUserApi = entitlementUserApi;
         this.factory = factory;
     }
-    
+
     public void processOverdueForAccount(UUID accountId) {
-        try {
-            Account account = accountUserApi.getAccountById(accountId);
-            processOverdue(account);
-        } catch (AccountApiException e) {
-            log.error("Error processing Overdue for Account with id: " + accountId.toString(), e);
+        List<SubscriptionBundle> bundles = entitlementUserApi.getBundlesForAccount(accountId);
+        for(SubscriptionBundle bundle : bundles) {
+            processOverdue(bundle);
         }
     }
-    
+
     public void processOverdueForBundle(UUID bundleId) {
         try {
             SubscriptionBundle bundle        = entitlementUserApi.getBundleFromId(bundleId);
@@ -68,20 +62,24 @@ public class OverdueDispatcher {
         }
     }
 
-    public void processOverdue(Blockable bloackable) {
+    public void processOverdue(Blockable blockable) {
         try {
-            OverdueWrapper<?> wrapper = factory.createOverdueWrapperFor(bloackable);
-            wrapper.refresh();
+            factory.createOverdueWrapperFor(blockable).refresh();
         } catch (OverdueError e) {
-            log.error("Error processing Overdue for Blockable with id: " + bloackable.getId().toString(), e);
+            log.error("Error processing Overdue for Blockable with id: " + blockable.getId().toString(), e);
         } catch (OverdueApiException e) {
-            log.error("Error processing Overdue for Blockable with id: " + bloackable.getId().toString(), e);
+            log.error("Error processing Overdue for Blockable with id: " + blockable.getId().toString(), e);
         }
     }
 
     public void processOverdue(UUID blockableId) {
-        
-        
+        try {
+        factory.createOverdueWrapperFor(blockableId).refresh();
+        } catch (OverdueError e) {
+            log.error("Error processing Overdue for Blockable with id: " + blockableId.toString(), e);
+        } catch (OverdueApiException e) {
+            log.error("Error processing Overdue for Blockable with id: " + blockableId.toString(), e);
+        }
     }
 
 }
diff --git a/overdue/src/main/java/com/ning/billing/overdue/listener/OverdueListener.java b/overdue/src/main/java/com/ning/billing/overdue/listener/OverdueListener.java
index 2c193b4..f208813 100644
--- a/overdue/src/main/java/com/ning/billing/overdue/listener/OverdueListener.java
+++ b/overdue/src/main/java/com/ning/billing/overdue/listener/OverdueListener.java
@@ -24,17 +24,14 @@ import org.slf4j.LoggerFactory;
 import com.google.common.eventbus.Subscribe;
 import com.google.inject.Inject;
 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;
 
 public class OverdueListener {
     OverdueDispatcher dispatcher;
-    
-    //
-    //TODO disabled overdue for prod deployment - comments should be removed
-    //
-    
+
     private final static Logger log = LoggerFactory.getLogger(OverdueListener.class);
     private final PaymentApi paymentApi;
 
@@ -46,21 +43,28 @@ public class OverdueListener {
 
     @Subscribe
     public void handlePaymentInfoEvent(final PaymentInfoEvent event) {
-//       String paymentId = event.getPaymentId();
-//       PaymentAttempt attempt = paymentApi.getPaymentAttemptForPaymentId(paymentId);
-//       UUID accountId = attempt.getAccountId();
-//       dispatcher.processOverdueForAccount(accountId);
+        log.info(String.format("Received PaymentInfo event %s", event.toString()));
+        try {
+            String paymentId = event.getPaymentId();
+            PaymentAttempt attempt = paymentApi.getPaymentAttemptForPaymentId(paymentId);
+            UUID accountId = attempt.getAccountId();
+            dispatcher.processOverdueForAccount(accountId);
+        } catch (PaymentApiException e) {
+            log.error("Payment exception encountered when trying process Overdue against payement: " + event.getPaymentId(), e);
+        }
     }
-    
+
     @Subscribe
     public void handlePaymentErrorEvent(final PaymentErrorEvent event) {
-//       UUID accountId = event.getAccountId();
-//       dispatcher.processOverdueForAccount(accountId);
+        log.info(String.format("Received PaymentError event %s", event.toString()));
+        UUID accountId = event.getAccountId();
+        dispatcher.processOverdueForAccount(accountId);
     }
 
     public void handleNextOverdueCheck(UUID overdueableId) { 
-//       dispatcher.processOverdue(overdueableId);
+        log.info(String.format("Received OD checkup notification for %s", overdueableId));
+        dispatcher.processOverdue(overdueableId);
     }
-    
- 
+
+
 }
diff --git a/overdue/src/main/java/com/ning/billing/overdue/service/DefaultOverdueService.java b/overdue/src/main/java/com/ning/billing/overdue/service/DefaultOverdueService.java
index 3153f0e..17f4be1 100644
--- a/overdue/src/main/java/com/ning/billing/overdue/service/DefaultOverdueService.java
+++ b/overdue/src/main/java/com/ning/billing/overdue/service/DefaultOverdueService.java
@@ -18,28 +18,48 @@ package com.ning.billing.overdue.service;
 
 import java.net.URI;
 
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
 import com.google.inject.Inject;
 import com.ning.billing.lifecycle.LifecycleHandlerType;
 import com.ning.billing.lifecycle.LifecycleHandlerType.LifecycleLevel;
+import com.ning.billing.ovedue.notification.OverdueCheckNotifier;
 import com.ning.billing.overdue.OverdueProperties;
 import com.ning.billing.overdue.OverdueUserApi;
 import com.ning.billing.overdue.config.OverdueConfig;
+import com.ning.billing.overdue.listener.OverdueListener;
+import com.ning.billing.util.bus.Bus.EventBusException;
+import com.ning.billing.util.bus.BusService;
 import com.ning.billing.util.config.XMLLoader;
 
 public class DefaultOverdueService implements ExtendedOverdueService {
+    private static final Logger log = LoggerFactory.getLogger(DefaultOverdueService.class);
+    
     public static final String OVERDUE_SERVICE_NAME = "overdue-service";
     private OverdueUserApi userApi;
     private OverdueConfig overdueConfig;
     private OverdueProperties properties;
+    private OverdueCheckNotifier notifier;
+    private BusService busService;
+    OverdueListener listener;
 
     private boolean isInitialized;
 
     @Inject
-    public DefaultOverdueService(OverdueUserApi userApi, OverdueProperties properties){
+    public DefaultOverdueService(
+            OverdueUserApi userApi, 
+            OverdueProperties properties, 
+            OverdueCheckNotifier notifier, 
+            BusService busService,
+            OverdueListener listener){
         this.userApi = userApi;
         this.properties = properties;
+        this.notifier = notifier;
+        this.busService = busService;
+        this.listener = listener;
     }
-    
+
     @Override
     public String getName() {
         return OVERDUE_SERVICE_NAME;
@@ -51,7 +71,7 @@ public class DefaultOverdueService implements ExtendedOverdueService {
     }
 
     @Override
-   public OverdueConfig getOverdueConfig() {
+    public OverdueConfig getOverdueConfig() {
         return overdueConfig;
     }
 
@@ -70,4 +90,36 @@ public class DefaultOverdueService implements ExtendedOverdueService {
         }
     }
 
+    @LifecycleHandlerType(LifecycleHandlerType.LifecycleLevel.INIT_SERVICE)
+    public void initialize() {
+        notifier.initialize();
+    }
+
+    @LifecycleHandlerType(LifecycleLevel.START_SERVICE)
+    public void start() {
+        notifier.start();
+    }
+
+    @LifecycleHandlerType(LifecycleHandlerType.LifecycleLevel.REGISTER_EVENTS)
+    public void registerForBus() {
+        try {
+            busService.getBus().register(listener);
+        } catch (EventBusException e) {
+           log.error("Problem encountered registering OverdueListener on the Event Bus", e);
+        }
+    }
+
+    @LifecycleHandlerType(LifecycleHandlerType.LifecycleLevel.UNREGISTER_EVENTS)
+    public void unregisterForBus() {
+        try {
+            busService.getBus().unregister(listener);
+        } catch (EventBusException e) {
+            log.error("Problem encountered registering OverdueListener on the Event Bus", e);
+        }
+    }
+
+    @LifecycleHandlerType(LifecycleLevel.STOP_SERVICE)
+    public void stop() {
+        notifier.stop();
+    }
 }
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 97220d5..cb693e4 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
@@ -56,8 +56,8 @@ public class OverdueWrapper<T extends Blockable> {
             String previousOverdueStateName = api.getBlockingStateFor(overdueable).getStateName();
             nextOverdueState                = overdueStateSet.calculateOverdueState(billingState, clock.getUTCNow());
 
-            if(!previousOverdueStateName.equals(nextOverdueState.getName())) {
-                overdueStateApplicator.apply(overdueable, nextOverdueState, nextOverdueState); 
+            if(nextOverdueState != null && !previousOverdueStateName.equals(nextOverdueState.getName())) {
+                overdueStateApplicator.apply(overdueable, previousOverdueStateName, nextOverdueState); 
             }
 
             return nextOverdueState;
diff --git a/overdue/src/main/java/com/ning/billing/overdue/wrapper/OverdueWrapperFactory.java b/overdue/src/main/java/com/ning/billing/overdue/wrapper/OverdueWrapperFactory.java
index 0fd6854..17e2817 100644
--- a/overdue/src/main/java/com/ning/billing/overdue/wrapper/OverdueWrapperFactory.java
+++ b/overdue/src/main/java/com/ning/billing/overdue/wrapper/OverdueWrapperFactory.java
@@ -39,7 +39,7 @@ import com.ning.billing.util.clock.Clock;
 public class OverdueWrapperFactory {
     private static final Logger log =  LoggerFactory.getLogger(OverdueWrapperFactory.class);
 
-    private final OverdueConfig overdueConfig;
+    protected  OverdueConfig overdueConfig; //protected and not final so it can be set in testing
     private final EntitlementUserApi entitlementApi;
     private final BillingStateCalculatorBundle billingStateCalcuatorBundle;
     private final OverdueStateApplicator<SubscriptionBundle> overdueStateApplicatorBundle;
@@ -47,14 +47,14 @@ public class OverdueWrapperFactory {
     private final Clock clock;
 
     @Inject
-    public OverdueWrapperFactory(BlockingApi api, ExtendedOverdueService service, Clock clock, 
+    public OverdueWrapperFactory(BlockingApi api, OverdueConfig config, Clock clock, 
             BillingStateCalculatorBundle billingStateCalcuatorBundle, 
             OverdueStateApplicator<SubscriptionBundle> overdueStateApplicatorBundle,
             EntitlementUserApi entitlementApi) {
         this.billingStateCalcuatorBundle = billingStateCalcuatorBundle;
         this.overdueStateApplicatorBundle = overdueStateApplicatorBundle;
         this.entitlementApi = entitlementApi;
-        this.overdueConfig = service.getOverdueConfig();
+        this.overdueConfig = config;
         this.api = api;
         this.clock = clock;
     }
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 bb7ece3..5d2061b 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
@@ -135,7 +135,48 @@ public class TestBillingStateCalculatorBundle extends TestBillingStateCalculator
     }
     
     
-    
+    @Test(groups = {"fast"}, enabled=true)
+    public void testcalculateBillingStateForBundleNoOverdueInvoices() throws Exception {
+        
+       UUID thisBundleId = new UUID(0L,0L);
+       UUID thatBundleId = new UUID(0L,1L);
+       
+       now = new DateTime();
+       List<Invoice> invoices = new ArrayList<Invoice>(5);
+      
+       Clock clock = new ClockMock();
+       InvoiceUserApi invoiceApi = BrainDeadProxyFactory.createBrainDeadProxyFor(InvoiceUserApi.class);
+       ((ZombieControl)invoiceApi).addResult("getUnpaidInvoicesByAccountId", invoices);
+       
+       SubscriptionBundle bundle = BrainDeadProxyFactory.createBrainDeadProxyFor(SubscriptionBundle.class);
+       ((ZombieControl)bundle).addResult("getId", thisBundleId);
+       ((ZombieControl)bundle).addResult("getAccountId", UUID.randomUUID());
+       
+       EntitlementUserApi entitlementApi = BrainDeadProxyFactory.createBrainDeadProxyFor(EntitlementUserApi.class);
+       Subscription subscription = BrainDeadProxyFactory.createBrainDeadProxyFor(Subscription.class);
+       ((ZombieControl)entitlementApi).addResult("getBaseSubscription",subscription);
+       
+       Plan plan = MockPlan.createBicycleNoTrialEvergreen1USD();
+       PriceList pricelist = new MockPriceList();
+       ((ZombieControl)subscription).addResult("getCurrentPlan", plan);
+       ((ZombieControl)subscription).addResult("getCurrentPriceList", pricelist);
+       ((ZombieControl)subscription).addResult("getCurrentPhase", plan.getFinalPhase());
+      
+       BillingStateCalculatorBundle calc = new BillingStateCalculatorBundle(entitlementApi, invoiceApi, clock);
+            
+       BillingStateBundle state = calc.calculateBillingState(bundle); 
+       
+       Assert.assertEquals(state.getNumberOfUnpaidInvoices(),0);
+       Assert.assertEquals(state.getBalanceOfUnpaidInvoices().intValue(), 0);
+       Assert.assertEquals(state.getDateOfEarliestUnpaidInvoice(), null);
+       Assert.assertEquals(state.getResponseForLastFailedPayment(),PaymentResponse.INSUFFICIENT_FUNDS); //TODO needs more when implemented
+       Assert.assertEquals(state.getTags().length,0);//TODO needs more when implemented
+       Assert.assertEquals(state.getBasePlanBillingPeriod(), plan.getBillingPeriod());
+       Assert.assertEquals(state.getBasePlanPhaseType(), plan.getFinalPhase().getPhaseType());
+       Assert.assertEquals(state.getBasePlanPriceList(), pricelist);
+       Assert.assertEquals(state.getBasePlanProduct(), plan.getProduct());
+       
+    }
     
     
     
diff --git a/overdue/src/test/java/com/ning/billing/overdue/config/TestCondition.java b/overdue/src/test/java/com/ning/billing/overdue/config/TestCondition.java
index 1e5f1e0..e697f29 100644
--- a/overdue/src/test/java/com/ning/billing/overdue/config/TestCondition.java
+++ b/overdue/src/test/java/com/ning/billing/overdue/config/TestCondition.java
@@ -89,7 +89,7 @@ public class TestCondition {
 		
 		DateTime now = new DateTime();
 		
-		BillingState<Blockable> state0 = new BillingState<Blockable>(new UUID(0L,1L), 0, BigDecimal.ZERO, now, PaymentResponse.INSUFFICIENT_FUNDS, new Tag[]{});
+		BillingState<Blockable> state0 = new BillingState<Blockable>(new UUID(0L,1L), 0, BigDecimal.ZERO, null, PaymentResponse.INSUFFICIENT_FUNDS, new Tag[]{});
 		BillingState<Blockable> state1 = new BillingState<Blockable>(new UUID(0L,1L), 1, new BigDecimal("100.00"), now.minusDays(10), PaymentResponse.INSUFFICIENT_FUNDS, new Tag[]{});
 		BillingState<Blockable> state2 = new BillingState<Blockable>(new UUID(0L,1L), 1, new BigDecimal("200.00"), now.minusDays(20), PaymentResponse.INSUFFICIENT_FUNDS, new Tag[]{});
 		
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
index df4f4c3..4cffcdc 100644
--- a/overdue/src/test/java/com/ning/billing/overdue/config/TestOverdueConfig.java
+++ b/overdue/src/test/java/com/ning/billing/overdue/config/TestOverdueConfig.java
@@ -36,6 +36,9 @@ public class TestOverdueConfig {
                     "           <externalMessage>Reached OD1</externalMessage>" +
                     "           <blockChanges>true</blockChanges>" +
                     "           <disableEntitlementAndChangesBlocked>false</disableEntitlementAndChangesBlocked>" +
+                    "           <autoReevaluationInterval>" +
+                    "               <unit>DAYS</unit><number>15</number>" +
+                    "           </autoReevaluationInterval>" +
                     "       </state>" +
                     "       <state name=\"OD2\">" +
                     "           <condition>" +
@@ -46,6 +49,9 @@ public class TestOverdueConfig {
                     "           <externalMessage>Reached OD1</externalMessage>" +
                     "           <blockChanges>true</blockChanges>" +
                     "           <disableEntitlementAndChangesBlocked>true</disableEntitlementAndChangesBlocked>" +
+                    "           <autoReevaluationInterval>" +
+                    "               <unit>DAYS</unit><number>15</number>" +
+                    "           </autoReevaluationInterval>" +
                     "       </state>" +
                     "   </bundleOverdueStates>" +
                     "</overdueConfig>";
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 7f48dda..a4d0fa1 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
@@ -53,7 +53,7 @@ import com.ning.billing.ovedue.notification.DefaultOverdueCheckNotifier;
 import com.ning.billing.ovedue.notification.DefaultOverdueCheckPoster;
 import com.ning.billing.ovedue.notification.OverdueCheckPoster;
 import com.ning.billing.overdue.OverdueProperties;
-import com.ning.billing.overdue.glue.OverdueModule;
+import com.ning.billing.overdue.glue.DefaultOverdueModule;
 import com.ning.billing.overdue.listener.OverdueListener;
 import com.ning.billing.util.bus.Bus;
 import com.ning.billing.util.bus.InMemoryBus;
@@ -108,7 +108,7 @@ public class TestOverdueCheckNotifier {
 	@BeforeClass(groups={"slow"})
 	public void setup() throws ServiceException, IOException, ClassNotFoundException, SQLException {
 		//TestApiBase.loadSystemPropertiesFromClasspath("/entitlement.properties");
-        final Injector g = Guice.createInjector(Stage.PRODUCTION,  new OverdueModule() {
+        final Injector g = Guice.createInjector(Stage.PRODUCTION,  new DefaultOverdueModule() {
 			
             protected void configure() {
                 super.configure();
diff --git a/overdue/src/test/resources/OverdueConfigSchema.xsd b/overdue/src/test/resources/OverdueConfigSchema.xsd
index 256da08..bf57ece 100644
--- a/overdue/src/test/resources/OverdueConfigSchema.xsd
+++ b/overdue/src/test/resources/OverdueConfigSchema.xsd
@@ -35,10 +35,11 @@
 <xs:sequence>
 <xs:element minOccurs="0" name="condition" type="defaultCondition"/>
 <xs:element minOccurs="0" name="externalMessage" type="xs:string"/>
-<xs:element minOccurs="0" name="disableEntitlementAndChangesBlocked" type="xs:boolean"/>
 <xs:element minOccurs="0" name="blockChanges" type="xs:boolean"/>
+<xs:element minOccurs="0" name="disableEntitlementAndChangesBlocked" type="xs:boolean"/>
 <xs:element minOccurs="0" name="daysBetweenPaymentRetries" type="xs:int"/>
 <xs:element minOccurs="0" name="isClearState" type="xs:boolean"/>
+<xs:element minOccurs="0" name="autoReevaluationInterval" type="defaultDuration"/>
 </xs:sequence>
 <xs:attribute name="name" type="xs:ID" use="required"/>
 </xs:extension>
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 2077c5e..c90d859 100644
--- a/payment/src/main/java/com/ning/billing/payment/RequestProcessor.java
+++ b/payment/src/main/java/com/ning/billing/payment/RequestProcessor.java
@@ -23,7 +23,6 @@ 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;
@@ -31,7 +30,6 @@ 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;
@@ -39,7 +37,6 @@ 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;
@@ -67,7 +64,7 @@ public class RequestProcessor {
         this.paymentApi = paymentApi;
         this.eventBus = eventBus;
     }
-    
+
     private void postPaymentEvent(BusEvent ev, UUID accountId) {
         if (ev == null) {
             return;
@@ -94,14 +91,14 @@ public class RequestProcessor {
                 postPaymentEvent(infoEvent, account.getId());
                 return;
             } else {
-                errorEvent = new DefaultPaymentErrorEvent(null, "Failed to retrieve account", event.getAccountId(), null, null);
+                errorEvent = new DefaultPaymentErrorEvent(null, "Failed to retrieve account", event.getAccountId(), event.getInvoiceId(), event.getUserToken());
             }
         } catch(AccountApiException e) {
             log.error("Failed to process invoice payment", e);
-            errorEvent = new DefaultPaymentErrorEvent(null, e.getMessage(), event.getAccountId(), null, null);            
+            errorEvent = new DefaultPaymentErrorEvent(null, e.getMessage(), event.getAccountId(), event.getInvoiceId(), event.getUserToken());            
         } catch (PaymentApiException e) {
-            log.error("Failed to process invoice payment", e);
-            errorEvent = new DefaultPaymentErrorEvent(null, e.getMessage(), event.getAccountId(), null, null);                        
+            log.info("Failed to process invoice payment for account: " + event.getAccountId()); // Removed stack trace log here and changed to info - this an expected flow
+            errorEvent = new DefaultPaymentErrorEvent(null, e.getMessage(), event.getAccountId(), event.getInvoiceId(), event.getUserToken());                        
         }
         postPaymentEvent(errorEvent, event.getAccountId());
     }
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 492bf30..f1176f7 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,6 +44,7 @@ import com.ning.billing.util.clock.Clock;
 
 public class MockPaymentProviderPlugin implements PaymentProviderPlugin {
     private final AtomicBoolean makeNextInvoiceFail = new AtomicBoolean(false);
+    private final AtomicBoolean makeAllInvoicesFail = new AtomicBoolean(false);
     private final Map<String, PaymentInfoEvent> payments = new ConcurrentHashMap<String, PaymentInfoEvent>();
     private final Map<String, PaymentProviderAccount> accounts = new ConcurrentHashMap<String, PaymentProviderAccount>();
     private final Map<String, PaymentMethodInfo> paymentMethods = new ConcurrentHashMap<String, PaymentMethodInfo>();
@@ -58,9 +59,13 @@ public class MockPaymentProviderPlugin implements PaymentProviderPlugin {
         makeNextInvoiceFail.set(true);
     }
 
+    public void makeAllInvoicesFail(boolean failure) {
+        makeAllInvoicesFail.set(failure);
+    }
+
     @Override
     public Either<PaymentErrorEvent, PaymentInfoEvent> processInvoice(Account account, Invoice invoice) {
-        if (makeNextInvoiceFail.getAndSet(false)) {
+        if (makeNextInvoiceFail.getAndSet(false) || makeAllInvoicesFail.get()) {
             return Either.left((PaymentErrorEvent) new DefaultPaymentErrorEvent("unknown", "test error", account.getId(), invoice.getId(), null));
         }
         else {
diff --git a/util/src/test/java/com/ning/billing/api/TestApiListener.java b/util/src/test/java/com/ning/billing/api/TestApiListener.java
index 351affc..9eddf7b 100644
--- a/util/src/test/java/com/ning/billing/api/TestApiListener.java
+++ b/util/src/test/java/com/ning/billing/api/TestApiListener.java
@@ -139,7 +139,8 @@ public class TestApiListener {
     @Subscribe
     public void handlePaymentErrorEvents(PaymentErrorEvent event) {
         log.info(String.format("TestApiListener Got PaymentError event %s", event.toString()));
-    }
+        assertEqualsNicely(NextEvent.PAYMENT_ERROR);
+        notifyIfStackEmpty();   }
 
     public void reset() {
         synchronized(this) {
@@ -149,6 +150,12 @@ public class TestApiListener {
         }
     }
 
+    public void pushExpectedEvents(NextEvent ... events) {
+        for(NextEvent event : events) {
+            pushExpectedEvent(event);
+        }
+    }
+
     public void pushExpectedEvent(NextEvent next) {
         synchronized (this) {
             Joiner joiner = Joiner.on(" ");