killbill-memoizeit

Fix issue in OverdueCode where we miss the first transition Implement

9/7/2012 7:22:57 PM

Changes

Details

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 9c280fe..cde8803 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
@@ -45,20 +45,23 @@ public interface Subscription extends Entity, Blockable {
         TRANSFERED
     }
 
-    public boolean cancel(DateTime requestedDate, boolean eot, CallContext context)
-            throws EntitlementUserApiException;
+    public boolean cancel(final DateTime requestedDate, final CallContext context)
+        throws EntitlementUserApiException;
+
+    public boolean cancelWithPolicy(final DateTime requestedDate, final ActionPolicy policy, final CallContext context)
+        throws EntitlementUserApiException;
 
-    public boolean uncancel(CallContext context)
+    public boolean uncancel(final CallContext context)
             throws EntitlementUserApiException;
 
-    public boolean changePlan(String productName, BillingPeriod term, String priceList, DateTime requestedDate, CallContext context)
+    public boolean changePlan(final String productName, final BillingPeriod term, final String priceList, final DateTime requestedDate, final CallContext context)
             throws EntitlementUserApiException;
 
-    public boolean changePlanWithPolicy(String productName, BillingPeriod term, String priceList, DateTime requestedDate,
-                                        ActionPolicy policy, CallContext context)
+    public boolean changePlanWithPolicy(final String productName, final BillingPeriod term, final String priceList, final DateTime requestedDate,
+            final ActionPolicy policy, final CallContext context)
             throws EntitlementUserApiException;
 
-    public boolean recreate(PlanPhaseSpecifier spec, DateTime requestedDate, CallContext context)
+    public boolean recreate(final PlanPhaseSpecifier spec, final DateTime requestedDate, final CallContext context)
             throws EntitlementUserApiException;
 
     public UUID getBundleId();
diff --git a/api/src/main/java/com/ning/billing/junction/api/Blockable.java b/api/src/main/java/com/ning/billing/junction/api/Blockable.java
index 73be651..cb5b1a0 100644
--- a/api/src/main/java/com/ning/billing/junction/api/Blockable.java
+++ b/api/src/main/java/com/ning/billing/junction/api/Blockable.java
@@ -18,7 +18,6 @@ package com.ning.billing.junction.api;
 
 import java.util.UUID;
 
-import com.ning.billing.ErrorCode;
 import com.ning.billing.account.api.Account;
 import com.ning.billing.entitlement.api.user.Subscription;
 import com.ning.billing.entitlement.api.user.SubscriptionBundle;
@@ -30,7 +29,7 @@ public interface Blockable {
         SUBSCRIPTION_BUNDLE,
         SUBSCRIPTION;
 
-        public static Type get(final Blockable o) throws BlockingApiException {
+        public static Type get(final Blockable o) {
             if (o instanceof Account) {
                 return ACCOUNT;
             } else if (o instanceof SubscriptionBundle) {
@@ -38,7 +37,7 @@ public interface Blockable {
             } else if (o instanceof Subscription) {
                 return SUBSCRIPTION;
             }
-            throw new BlockingApiException(ErrorCode.BLOCK_TYPE_NOT_SUPPORTED, o.getClass().getName());
+            throw new IllegalStateException("Unsupported type of blockable " + o);
         }
 
         public static Type get(final String type) throws BlockingApiException {
@@ -49,7 +48,7 @@ public interface Blockable {
             } else if (type.equalsIgnoreCase(SUBSCRIPTION.name())) {
                 return SUBSCRIPTION;
             }
-            throw new BlockingApiException(ErrorCode.BLOCK_TYPE_NOT_SUPPORTED, type);
+            throw new IllegalStateException("Unsupported type of blockable " + type);
         }
 
     }
diff --git a/api/src/main/java/com/ning/billing/overdue/Condition.java b/api/src/main/java/com/ning/billing/overdue/Condition.java
index 8319b48..f5fe347 100644
--- a/api/src/main/java/com/ning/billing/overdue/Condition.java
+++ b/api/src/main/java/com/ning/billing/overdue/Condition.java
@@ -16,7 +16,9 @@
 
 package com.ning.billing.overdue;
 
+
 import org.joda.time.LocalDate;
+import org.joda.time.Period;
 
 import com.ning.billing.junction.api.Blockable;
 import com.ning.billing.overdue.config.api.BillingState;
@@ -31,5 +33,4 @@ public interface Condition<T extends Blockable> {
      * @return true if the condition is true, false otherwise
      */
     public boolean evaluate(BillingState<T> state, LocalDate now);
-
 }
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 e423d10..b0218d7 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
@@ -39,4 +39,6 @@ public interface OverdueStateSet<T extends Blockable> {
     public abstract OverdueState<T> calculateOverdueState(BillingState<T> billingState, LocalDate now) throws OverdueApiException;
 
     public abstract int size();
+
+    public abstract OverdueState<T> getFirstState();
 }
diff --git a/api/src/main/java/com/ning/billing/overdue/OverdueCancellationPolicicy.java b/api/src/main/java/com/ning/billing/overdue/OverdueCancellationPolicicy.java
new file mode 100644
index 0000000..dda0dd6
--- /dev/null
+++ b/api/src/main/java/com/ning/billing/overdue/OverdueCancellationPolicicy.java
@@ -0,0 +1,22 @@
+/*
+ * 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;
+
+public enum OverdueCancellationPolicicy {
+    END_OF_TERM,
+    IMMEDIATE,
+    NONE
+}
diff --git a/api/src/main/java/com/ning/billing/overdue/OverdueState.java b/api/src/main/java/com/ning/billing/overdue/OverdueState.java
index 39cf93d..0c216b1 100644
--- a/api/src/main/java/com/ning/billing/overdue/OverdueState.java
+++ b/api/src/main/java/com/ning/billing/overdue/OverdueState.java
@@ -31,6 +31,8 @@ public interface OverdueState<T extends Blockable> {
 
     public boolean disableEntitlementAndChangesBlocked();
 
+    public OverdueCancellationPolicicy getSubscriptionCancellationPolicy();
+
     public boolean blockChanges();
 
     public boolean isClearState();
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 eca5b18..d06bd7e 100644
--- a/api/src/main/java/com/ning/billing/overdue/OverdueUserApi.java
+++ b/api/src/main/java/com/ning/billing/overdue/OverdueUserApi.java
@@ -18,16 +18,16 @@ package com.ning.billing.overdue;
 
 import com.ning.billing.junction.api.Blockable;
 import com.ning.billing.overdue.config.api.BillingState;
-import com.ning.billing.overdue.config.api.OverdueError;
+import com.ning.billing.overdue.config.api.OverdueException;
 
 public interface OverdueUserApi {
 
-    public <T extends Blockable> OverdueState<T> refreshOverdueStateFor(T overdueable) throws OverdueError, OverdueApiException;
+    public <T extends Blockable> OverdueState<T> refreshOverdueStateFor(T overdueable) throws OverdueException, OverdueApiException;
 
-    public <T extends Blockable> void setOverrideBillingStateForAccount(T overdueable, BillingState<T> state) throws OverdueError;
+    public <T extends Blockable> void setOverrideBillingStateForAccount(T overdueable, BillingState<T> state) throws OverdueException;
 
-    public <T extends Blockable> OverdueState<T> getOverdueStateFor(T overdueable) throws OverdueError;
+    public <T extends Blockable> OverdueState<T> getOverdueStateFor(T overdueable) throws OverdueException;
 
-    public <T extends Blockable> BillingState<T> getBillingStateFor(T overdueable) throws OverdueError;
+    public <T extends Blockable> BillingState<T> getBillingStateFor(T overdueable) throws OverdueException;
 
 }
diff --git a/beatrix/src/test/java/com/ning/billing/beatrix/integration/overdue/TestOverdueBase.java b/beatrix/src/test/java/com/ning/billing/beatrix/integration/overdue/TestOverdueBase.java
new file mode 100644
index 0000000..773a1eb
--- /dev/null
+++ b/beatrix/src/test/java/com/ning/billing/beatrix/integration/overdue/TestOverdueBase.java
@@ -0,0 +1,145 @@
+/*
+ * 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 com.jayway.awaitility.Awaitility.await;
+import static java.util.concurrent.TimeUnit.SECONDS;
+import static org.testng.Assert.assertNotNull;
+
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+import java.util.List;
+import java.util.UUID;
+import java.util.concurrent.Callable;
+
+import org.testng.Assert;
+import org.testng.annotations.BeforeMethod;
+
+import com.ning.billing.account.api.Account;
+import com.ning.billing.beatrix.integration.BeatrixModule;
+import com.ning.billing.beatrix.integration.TestIntegrationBase;
+import com.ning.billing.catalog.api.BillingPeriod;
+import com.ning.billing.entitlement.api.user.SubscriptionBundle;
+import com.ning.billing.invoice.api.InvoiceUserApi;
+import com.ning.billing.junction.api.BlockingApi;
+import com.ning.billing.overdue.OverdueUserApi;
+import com.ning.billing.overdue.config.OverdueConfig;
+import com.ning.billing.overdue.wrapper.OverdueWrapperFactory;
+import com.ning.billing.payment.api.PaymentApi;
+import com.ning.billing.payment.api.PaymentMethodPlugin;
+import com.ning.billing.payment.provider.MockPaymentProviderPlugin;
+import com.ning.billing.util.clock.ClockMock;
+import com.ning.billing.util.config.XMLLoader;
+
+import com.google.inject.Inject;
+import com.google.inject.name.Named;
+
+public abstract class TestOverdueBase extends TestIntegrationBase {
+
+    @Inject
+    protected ClockMock clock;
+
+    @Named("yoyo")
+    @Inject
+    protected
+    MockPaymentProviderPlugin paymentPlugin;
+
+    @Inject
+    protected BlockingApi blockingApi;
+
+    @Inject
+    protected OverdueWrapperFactory overdueWrapperFactory;
+
+    @Inject
+    protected OverdueUserApi overdueApi;
+
+    @Inject
+    protected PaymentApi paymentApi;
+
+    @Inject
+    protected InvoiceUserApi invoiceApi;
+
+    protected Account account;
+    protected SubscriptionBundle bundle;
+    protected String productName;
+    protected BillingPeriod term;
+
+
+    public abstract String getOverdueConfig();
+
+    final PaymentMethodPlugin paymentMethodPlugin = new PaymentMethodPlugin() {
+        @Override
+        public boolean isDefaultPaymentMethod() {
+            return false;
+        }
+
+        @Override
+        public String getValueString(final String key) {
+            return null;
+        }
+
+        @Override
+        public List<PaymentMethodKVInfo> getProperties() {
+            return null;
+        }
+
+        @Override
+        public String getExternalPaymentMethodId() {
+            return UUID.randomUUID().toString();
+        }
+    };
+
+    @BeforeMethod(groups = "slow")
+    public void setupOverdue() throws Exception {
+        final String configXml = getOverdueConfig();
+        final InputStream is = new ByteArrayInputStream(configXml.getBytes());
+        final OverdueConfig config = XMLLoader.getObjectFromStreamNoValidation(is, OverdueConfig.class);
+        overdueWrapperFactory.setOverdueConfig(config);
+
+        account = createAccountWithPaymentMethod(getAccountData(0));
+        assertNotNull(account);
+
+        paymentApi.addPaymentMethod(BeatrixModule.PLUGIN_NAME, account, true, paymentMethodPlugin, context);
+
+        bundle = entitlementUserApi.createBundleForAccount(account.getId(), "whatever", context);
+
+        productName = "Shotgun";
+        term = BillingPeriod.MONTHLY;
+
+        paymentPlugin.clear();
+    }
+
+
+    protected void checkODState(final String expected) {
+        try {
+            // This will test the overdue notification queue: when we move the clock, the overdue system
+            // should get notified to refresh its state.
+            // Calling explicitly refresh here (overdueApi.refreshOverdueStateFor(bundle)) would not fully
+            // test overdue.
+            // Since we're relying on the notification queue, we may need to wait a bit (hence await()).
+            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, "Got exception: " + e.toString());
+        }
+    }
+
+
+}
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 420516c..6aa69ad 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,25 +16,18 @@
 
 package com.ning.billing.beatrix.integration.overdue;
 
-import java.io.ByteArrayInputStream;
-import java.io.InputStream;
 import java.math.BigDecimal;
 import java.util.Collection;
-import java.util.List;
-import java.util.UUID;
-import java.util.concurrent.Callable;
+
 
 import org.joda.time.DateTime;
 import org.joda.time.LocalDate;
-import org.testng.Assert;
-import org.testng.annotations.BeforeMethod;
+
 import org.testng.annotations.Guice;
 import org.testng.annotations.Test;
 
-import com.ning.billing.account.api.Account;
 import com.ning.billing.api.TestApiListener.NextEvent;
 import com.ning.billing.beatrix.integration.BeatrixModule;
-import com.ning.billing.beatrix.integration.TestIntegrationBase;
 import com.ning.billing.beatrix.util.InvoiceChecker.ExpectedItemCheck;
 import com.ning.billing.catalog.api.BillingPeriod;
 import com.ning.billing.catalog.api.Currency;
@@ -42,149 +35,72 @@ import com.ning.billing.catalog.api.PriceListSet;
 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.SubscriptionBundle;
 import com.ning.billing.invoice.api.Invoice;
 import com.ning.billing.invoice.api.InvoiceItemType;
 import com.ning.billing.invoice.api.InvoicePayment;
-import com.ning.billing.invoice.api.InvoiceUserApi;
-import com.ning.billing.junction.api.Blockable;
 import com.ning.billing.junction.api.BlockingApi;
 import com.ning.billing.junction.api.BlockingApiException;
-import com.ning.billing.overdue.OverdueUserApi;
-import com.ning.billing.overdue.config.OverdueConfig;
-import com.ning.billing.overdue.wrapper.OverdueWrapperFactory;
 import com.ning.billing.payment.api.Payment;
-import com.ning.billing.payment.api.PaymentApi;
-import com.ning.billing.payment.api.PaymentMethodPlugin;
-import com.ning.billing.payment.provider.MockPaymentProviderPlugin;
-import com.ning.billing.util.clock.ClockMock;
-import com.ning.billing.util.config.XMLLoader;
 
-import com.google.inject.Inject;
-import com.google.inject.name.Named;
-
-import static com.jayway.awaitility.Awaitility.await;
-import static java.util.concurrent.TimeUnit.SECONDS;
 import static org.testng.Assert.assertEquals;
-import static org.testng.Assert.assertNotNull;
 import static org.testng.Assert.assertTrue;
 
 @Test(groups = "slow")
 @Guice(modules = {BeatrixModule.class})
-public class TestOverdueIntegration extends TestIntegrationBase {
-
-    @Inject
-    private ClockMock clock;
-
-    @Named("yoyo")
-    @Inject
-    private
-    MockPaymentProviderPlugin paymentPlugin;
-
-    @Inject
-    private BlockingApi blockingApi;
-
-    @Inject
-    private OverdueWrapperFactory overdueWrapperFactory;
-
-    @Inject
-    private OverdueUserApi overdueApi;
-
-    @Inject
-    private PaymentApi paymentApi;
-
-    @Inject
-    private InvoiceUserApi invoiceApi;
-
-    private Account account;
-    private SubscriptionBundle bundle;
-    private String productName;
-    private BillingPeriod term;
-
-    final PaymentMethodPlugin paymentMethodPlugin = new PaymentMethodPlugin() {
-        @Override
-        public boolean isDefaultPaymentMethod() {
-            return false;
-        }
+public class TestOverdueIntegration extends TestOverdueBase {
 
-        @Override
-        public String getValueString(final String key) {
-            return null;
-        }
-
-        @Override
-        public List<PaymentMethodKVInfo> getProperties() {
-            return null;
-        }
 
-        @Override
-        public String getExternalPaymentMethodId() {
-            return UUID.randomUUID().toString();
-        }
-    };
 
-    @BeforeMethod(groups = "slow")
-    public void setupOverdue() throws Exception {
+    @Override
+    public String getOverdueConfig() {
         final String configXml = "<overdueConfig>" +
-                                 "   <bundleOverdueStates>" +
-                                 "       <state name=\"OD3\">" +
-                                 "           <condition>" +
-                                 "               <timeSinceEarliestUnpaidInvoiceEqualsOrExceeds>" +
-                                 "                   <unit>DAYS</unit><number>50</number>" +
-                                 "               </timeSinceEarliestUnpaidInvoiceEqualsOrExceeds>" +
-                                 "           </condition>" +
-                                 "           <externalMessage>Reached OD3</externalMessage>" +
-                                 "           <blockChanges>true</blockChanges>" +
-                                 "           <disableEntitlementAndChangesBlocked>true</disableEntitlementAndChangesBlocked>" +
-                                 "           <autoReevaluationInterval>" +
-                                 "               <unit>DAYS</unit><number>5</number>" +
-                                 "           </autoReevaluationInterval>" +
-                                 "       </state>" +
-                                 "       <state name=\"OD2\">" +
-                                 "           <condition>" +
-                                 "               <timeSinceEarliestUnpaidInvoiceEqualsOrExceeds>" +
-                                 "                   <unit>DAYS</unit><number>40</number>" +
-                                 "               </timeSinceEarliestUnpaidInvoiceEqualsOrExceeds>" +
-                                 "           </condition>" +
-                                 "           <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>5</number>" +
-                                 "           </autoReevaluationInterval>" +
-                                 "       </state>" +
-                                 "   </bundleOverdueStates>" +
-                                 "</overdueConfig>";
-        final InputStream is = new ByteArrayInputStream(configXml.getBytes());
-        final OverdueConfig config = XMLLoader.getObjectFromStreamNoValidation(is, OverdueConfig.class);
-        overdueWrapperFactory.setOverdueConfig(config);
-
-        account = createAccountWithPaymentMethod(getAccountData(0));
-        assertNotNull(account);
-
-        paymentApi.addPaymentMethod(BeatrixModule.PLUGIN_NAME, account, true, paymentMethodPlugin, context);
-
-        bundle = entitlementUserApi.createBundleForAccount(account.getId(), "whatever", context);
-
-        productName = "Shotgun";
-        term = BillingPeriod.MONTHLY;
-
-        paymentPlugin.clear();
+        "   <bundleOverdueStates>" +
+        "       <state name=\"OD3\">" +
+        "           <condition>" +
+        "               <timeSinceEarliestUnpaidInvoiceEqualsOrExceeds>" +
+        "                   <unit>DAYS</unit><number>50</number>" +
+        "               </timeSinceEarliestUnpaidInvoiceEqualsOrExceeds>" +
+        "           </condition>" +
+        "           <externalMessage>Reached OD3</externalMessage>" +
+        "           <blockChanges>true</blockChanges>" +
+        "           <disableEntitlementAndChangesBlocked>true</disableEntitlementAndChangesBlocked>" +
+        "           <autoReevaluationInterval>" +
+        "               <unit>DAYS</unit><number>5</number>" +
+        "           </autoReevaluationInterval>" +
+        "       </state>" +
+        "       <state name=\"OD2\">" +
+        "           <condition>" +
+        "               <timeSinceEarliestUnpaidInvoiceEqualsOrExceeds>" +
+        "                   <unit>DAYS</unit><number>40</number>" +
+        "               </timeSinceEarliestUnpaidInvoiceEqualsOrExceeds>" +
+        "           </condition>" +
+        "           <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>5</number>" +
+        "           </autoReevaluationInterval>" +
+        "       </state>" +
+        "   </bundleOverdueStates>" +
+        "</overdueConfig>";
+
+        return configXml;
     }
 
+
     // We set the the property killbill.payment.retry.days=8,8,8,8,8,8,8,8 so that Payment retry logic does not end with an ABORTED state
     // preventing final instant payment to succeed.
     @Test(groups = "slow")
@@ -645,22 +561,4 @@ public class TestOverdueIntegration extends TestIntegrationBase {
             changeSubscriptionAndCheckForCompletion(subscription, "Assault-Rifle", BillingPeriod.MONTHLY, NextEvent.CHANGE, NextEvent.INVOICE);
         }
     }
-
-    private void checkODState(final String expected) {
-        try {
-            // This will test the overdue notification queue: when we move the clock, the overdue system
-            // should get notified to refresh its state.
-            // Calling explicitly refresh here (overdueApi.refreshOverdueStateFor(bundle)) would not fully
-            // test overdue.
-            // Since we're relying on the notification queue, we may need to wait a bit (hence await()).
-            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, "Got exception: " + e.toString());
-        }
-    }
 }
diff --git a/beatrix/src/test/java/com/ning/billing/beatrix/integration/overdue/TestOverdueWithSubscriptionCancellation.java b/beatrix/src/test/java/com/ning/billing/beatrix/integration/overdue/TestOverdueWithSubscriptionCancellation.java
new file mode 100644
index 0000000..618162b
--- /dev/null
+++ b/beatrix/src/test/java/com/ning/billing/beatrix/integration/overdue/TestOverdueWithSubscriptionCancellation.java
@@ -0,0 +1,103 @@
+/*
+ * 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 junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertTrue;
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNotNull;
+
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+import java.math.BigDecimal;
+import java.util.Collection;
+import java.util.List;
+import java.util.UUID;
+
+import junit.framework.Assert;
+
+import org.joda.time.DateTime;
+import org.joda.time.LocalDate;
+import org.testng.annotations.Guice;
+import org.testng.annotations.Test;
+
+import com.ning.billing.api.TestApiListener.NextEvent;
+import com.ning.billing.beatrix.integration.BeatrixModule;
+import com.ning.billing.beatrix.util.InvoiceChecker.ExpectedItemCheck;
+import com.ning.billing.catalog.api.ProductCategory;
+import com.ning.billing.entitlement.api.user.Subscription;
+import com.ning.billing.entitlement.api.user.Subscription.SubscriptionState;
+import com.ning.billing.invoice.api.InvoiceItemType;
+import com.ning.billing.junction.api.BlockingApi;
+
+@Test(groups = "slow")
+@Guice(modules = {BeatrixModule.class})
+public class TestOverdueWithSubscriptionCancellation extends TestOverdueBase {
+
+
+    @Override
+    public String getOverdueConfig() {
+        final String configXml = "<overdueConfig>" +
+        "   <bundleOverdueStates>" +
+           "       <state name=\"OD1\">" +
+        "           <condition>" +
+        "               <timeSinceEarliestUnpaidInvoiceEqualsOrExceeds>" +
+        "                   <unit>DAYS</unit><number>5</number>" +
+        "               </timeSinceEarliestUnpaidInvoiceEqualsOrExceeds>" +
+        "           </condition>" +
+        "           <externalMessage>Reached OD1</externalMessage>" +
+        "           <blockChanges>true</blockChanges>" +
+        "           <disableEntitlementAndChangesBlocked>false</disableEntitlementAndChangesBlocked>" +
+        "           <subscriptionCancellationPolicy>IMMEDIATE</subscriptionCancellationPolicy>" +
+        "           <autoReevaluationInterval>" +
+        "               <unit>DAYS</unit><number>5</number>" +
+        "           </autoReevaluationInterval>" +
+        "       </state>" +
+        "   </bundleOverdueStates>" +
+        "</overdueConfig>";
+        return configXml;
+    }
+
+    @Test(groups = "slow")
+    public void testCheckSubscriptionCancellation() throws Exception {
+        clock.setTime(new DateTime(2012, 5, 1, 0, 3, 42, 0));
+
+        // Set next invoice to fail and create subscription
+        paymentPlugin.makeAllInvoicesFailWithError(true);
+        final Subscription baseSubscription = createSubscriptionAndCheckForCompletion(bundle.getId(), productName, ProductCategory.BASE, term, NextEvent.CREATE, NextEvent.INVOICE);
+
+        invoiceChecker.checkInvoice(account.getId(), 1, new ExpectedItemCheck(new LocalDate(2012, 5, 1), null, InvoiceItemType.FIXED, new BigDecimal("0")));
+        invoiceChecker.checkChargedThroughDate(baseSubscription.getId(), new LocalDate(2012, 5, 1));
+
+        // DAY 30 have to get out of trial before first payment
+        addDaysAndCheckForCompletion(30, NextEvent.PHASE, NextEvent.INVOICE, NextEvent.PAYMENT_ERROR);
+
+        invoiceChecker.checkInvoice(account.getId(), 2, new ExpectedItemCheck(new LocalDate(2012, 5, 31), new LocalDate(2012, 6, 30), InvoiceItemType.RECURRING, new BigDecimal("249.95")));
+        invoiceChecker.checkChargedThroughDate(baseSubscription.getId(), new LocalDate(2012, 6, 30));
+
+        // Should still be in clear state
+        checkODState(BlockingApi.CLEAR_STATE_NAME);
+
+        // DAY 36 -- RIGHT AFTER OD1
+        addDaysAndCheckForCompletion(6, NextEvent.CANCEL);
+
+        // Should be in OD1
+        checkODState("OD1");
+
+        Subscription cancelledBaseSubscription = entitlementUserApi.getSubscriptionFromId(baseSubscription.getId());
+        assertTrue(cancelledBaseSubscription.getState() == SubscriptionState.CANCELLED);
+    }
+}
diff --git a/beatrix/src/test/java/com/ning/billing/beatrix/integration/TestAnalytics.java b/beatrix/src/test/java/com/ning/billing/beatrix/integration/TestAnalytics.java
index 8728ac9..0dfcc78 100644
--- a/beatrix/src/test/java/com/ning/billing/beatrix/integration/TestAnalytics.java
+++ b/beatrix/src/test/java/com/ning/billing/beatrix/integration/TestAnalytics.java
@@ -158,7 +158,7 @@ public class TestAnalytics extends TestIntegrationBase {
         // Cancel end of term - refetch the subscription to have the CTD set
         // (otherwise, cancellation would be immediate)
         subscription = entitlementUserApi.getSubscriptionFromId(subscription.getId());
-        subscription.cancel(clock.getUTCNow(), true, context);
+        subscription.cancel(clock.getUTCNow(), context);
 
         waitALittle();
 
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 b5860e8..d9efb35 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
@@ -539,7 +539,7 @@ public class TestIntegration extends TestIntegrationBase {
         assertTrue(busHandler.isCompleted(DELAY));
 
         subscription = subscriptionDataFromSubscription(entitlementUserApi.getSubscriptionFromId(subscription.getId()));
-        subscription.cancel(clock.getUTCNow(), false, context);
+        subscription.cancel(clock.getUTCNow(), context);
 
         // MOVE AFTER CANCEL DATE AND EXPECT EVENT : NextEvent.CANCEL
         busHandler.pushExpectedEvent(NextEvent.CANCEL);
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 e9c42b0..900e621 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
@@ -20,6 +20,7 @@ import java.math.BigDecimal;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.UUID;
+import java.util.concurrent.Callable;
 
 import javax.annotation.Nullable;
 
@@ -60,6 +61,7 @@ import com.ning.billing.entitlement.api.transfer.EntitlementTransferApi;
 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.SubscriptionData;
 import com.ning.billing.invoice.api.Invoice;
 import com.ning.billing.invoice.api.InvoiceApiException;
@@ -69,6 +71,7 @@ import com.ning.billing.invoice.api.InvoicePaymentApi;
 import com.ning.billing.invoice.api.InvoiceService;
 import com.ning.billing.invoice.api.InvoiceUserApi;
 import com.ning.billing.invoice.model.InvoicingConfiguration;
+import com.ning.billing.junction.api.BlockingApi;
 import com.ning.billing.junction.plumbing.api.BlockingSubscription;
 import com.ning.billing.mock.MockAccountBuilder;
 import com.ning.billing.mock.api.MockBillCycleDay;
@@ -92,6 +95,8 @@ import com.google.common.base.Joiner;
 import com.google.inject.Inject;
 import com.google.inject.name.Named;
 
+import static com.jayway.awaitility.Awaitility.await;
+import static java.util.concurrent.TimeUnit.SECONDS;
 import static org.testng.Assert.assertEquals;
 import static org.testng.Assert.assertNotNull;
 import static org.testng.Assert.assertTrue;
@@ -456,7 +461,7 @@ public class TestIntegrationBase extends BeatrixTestSuiteWithEmbeddedDB implemen
                     refreshedSubscription.changePlan(productName, billingPeriod, PriceListSet.DEFAULT_PRICELIST_NAME, clock.getUTCNow(), context);
                     return refreshedSubscription;
                 } catch (EntitlementUserApiException e) {
-                    fail();
+                    fail(e.getMessage());
                     return null;
                 }
             }
@@ -472,7 +477,7 @@ public class TestIntegrationBase extends BeatrixTestSuiteWithEmbeddedDB implemen
                 try {
                     // Need to fetch again to get latest CTD updated from the system
                     final Subscription refreshedSubscription = entitlementUserApi.getSubscriptionFromId(subscription.getId());
-                    refreshedSubscription.cancel(requestedDate, true, context);
+                    refreshedSubscription.cancel(requestedDate, context);
                     return refreshedSubscription;
                 } catch (EntitlementUserApiException e) {
                     fail();
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 1e35021..4721c66 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
@@ -1,4 +1,4 @@
-/* 
+/*
  * Copyright 2010-2011 Ning, Inc.
  *
  * Ning licenses this file to you under the Apache License, version 2.0
@@ -38,8 +38,11 @@ public interface SubscriptionApiService {
     public boolean recreatePlan(SubscriptionData subscription, PlanPhaseSpecifier spec, DateTime requestedDate, CallContext context)
             throws EntitlementUserApiException;
 
-    public boolean cancel(SubscriptionData subscription, DateTime requestedDate, boolean eot, CallContext context)
-            throws EntitlementUserApiException;
+    public boolean cancel(SubscriptionData subscription, DateTime requestedDate, CallContext context)
+        throws EntitlementUserApiException;
+
+    public boolean cancelWithPolicy(SubscriptionData subscription, DateTime requestedDate, ActionPolicy policy, CallContext context)
+        throws EntitlementUserApiException;
 
     public boolean uncancel(SubscriptionData subscription, CallContext context)
             throws EntitlementUserApiException;
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 66cf27c..87c71f8 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
@@ -1,4 +1,4 @@
-/* 
+/*
  * Copyright 2010-2011 Ning, Inc.
  *
  * Ning licenses this file to you under the Apache License, version 2.0
@@ -99,7 +99,7 @@ public class SubscriptionDataRepair extends SubscriptionData {
                     trickleDownBPEffectForAddon(addonSubscriptions, getLastUserEventEffectiveDate(), context);
                     break;
                 case CANCEL:
-                    cancel(input.getRequestedDate(), false, context);
+                    cancel(input.getRequestedDate(), context);
                     trickleDownBPEffectForAddon(addonSubscriptions, getLastUserEventEffectiveDate(), context);
                     break;
                 case PHASE:
@@ -178,6 +178,7 @@ public class SubscriptionDataRepair extends SubscriptionData {
         }
     }
 
+    @Override
     public void rebuildTransitions(final List<EntitlementEvent> inputEvents, final Catalog catalog) {
         this.events = inputEvents;
         super.rebuildTransitions(inputEvents, catalog);
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 ac3e704..16814cb 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
@@ -154,16 +154,14 @@ public class DefaultSubscriptionApiService implements SubscriptionApiService {
     }
 
     @Override
-    public boolean cancel(final SubscriptionData subscription, final DateTime requestedDateWithMs, final boolean eot, final CallContext context) throws EntitlementUserApiException {
+    public boolean cancel(final SubscriptionData subscription, final DateTime requestedDateWithMs, final CallContext context) throws EntitlementUserApiException {
         try {
             final SubscriptionState currentState = subscription.getState();
             if (currentState != SubscriptionState.ACTIVE) {
                 throw new EntitlementUserApiException(ErrorCode.ENT_CANCEL_BAD_STATE, subscription.getId(), currentState);
             }
-
             final DateTime now = clock.getUTCNow();
             final DateTime requestedDate = (requestedDateWithMs != null) ? DefaultClock.truncateMs(requestedDateWithMs) : now;
-            validateRequestedDate(subscription, now, requestedDate);
 
             final Plan currentPlan = subscription.getCurrentPlan();
             final PlanPhaseSpecifier planPhase = new PlanPhaseSpecifier(currentPlan.getProduct().getName(),
@@ -173,26 +171,45 @@ public class DefaultSubscriptionApiService implements SubscriptionApiService {
                                                                         subscription.getCurrentPhase().getPhaseType());
 
             final ActionPolicy policy = catalogService.getFullCatalog().planCancelPolicy(planPhase, requestedDate);
-            final DateTime effectiveDate = subscription.getPlanChangeEffectiveDate(policy, requestedDate);
 
-            final EntitlementEvent cancelEvent = new ApiEventCancel(new ApiEventBuilder()
-            .setSubscriptionId(subscription.getId())
-            .setActiveVersion(subscription.getActiveVersion())
-            .setProcessedDate(now)
-            .setEffectiveDate(effectiveDate)
-            .setRequestedDate(requestedDate)
-            .setUserToken(context.getUserToken())
-            .setFromDisk(true));
-
-            dao.cancelSubscription(subscription, cancelEvent, context, 0);
-            subscription.rebuildTransitions(dao.getEventsForSubscription(subscription.getId()), catalogService.getFullCatalog());
-            return (policy == ActionPolicy.IMMEDIATE);
+            return doCancelPlan(subscription, requestedDateWithMs, now, policy, context);
         } catch (CatalogApiException e) {
             throw new EntitlementUserApiException(e);
         }
     }
 
     @Override
+    public boolean cancelWithPolicy(final SubscriptionData subscription, final DateTime requestedDateWithMs, final ActionPolicy policy, final CallContext context) throws EntitlementUserApiException {
+        final SubscriptionState currentState = subscription.getState();
+        if (currentState != SubscriptionState.ACTIVE) {
+            throw new EntitlementUserApiException(ErrorCode.ENT_CANCEL_BAD_STATE, subscription.getId(), currentState);
+        }
+        final DateTime now = clock.getUTCNow();
+        return doCancelPlan(subscription, requestedDateWithMs, now, policy, context);
+    }
+
+    private boolean doCancelPlan(final SubscriptionData subscription, final DateTime requestedDateWithMs, final DateTime now, final ActionPolicy policy, final CallContext context) throws EntitlementUserApiException {
+
+        final DateTime requestedDate = (requestedDateWithMs != null) ? DefaultClock.truncateMs(requestedDateWithMs) : now;
+        validateRequestedDate(subscription, now, requestedDate);
+        final DateTime effectiveDate = subscription.getPlanChangeEffectiveDate(policy, requestedDate);
+
+        final EntitlementEvent cancelEvent = new ApiEventCancel(new ApiEventBuilder()
+        .setSubscriptionId(subscription.getId())
+        .setActiveVersion(subscription.getActiveVersion())
+        .setProcessedDate(now)
+        .setEffectiveDate(effectiveDate)
+        .setRequestedDate(requestedDate)
+        .setUserToken(context.getUserToken())
+        .setFromDisk(true));
+
+        dao.cancelSubscription(subscription, cancelEvent, context, 0);
+        subscription.rebuildTransitions(dao.getEventsForSubscription(subscription.getId()), catalogService.getFullCatalog());
+        return (policy == ActionPolicy.IMMEDIATE);
+    }
+
+
+    @Override
     public boolean uncancel(final SubscriptionData subscription, final CallContext context) throws EntitlementUserApiException {
         if (!subscription.isSubscriptionFutureCancelled()) {
             throw new EntitlementUserApiException(ErrorCode.ENT_UNCANCEL_BAD_STATE, subscription.getId().toString());
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 db4f78f..4e8c923 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
@@ -181,9 +181,13 @@ public class SubscriptionData extends EntityBase implements Subscription {
 
 
     @Override
-    public boolean cancel(final DateTime requestedDate, final boolean eot,
-            final CallContext context) throws EntitlementUserApiException {
-        return apiService.cancel(this, requestedDate, eot, context);
+    public boolean cancel(final DateTime requestedDate, final CallContext context) throws EntitlementUserApiException {
+        return apiService.cancel(this, requestedDate, context);
+    }
+
+    @Override
+    public boolean cancelWithPolicy(final DateTime requestedDate, final ActionPolicy policy, final CallContext context) throws EntitlementUserApiException {
+        return apiService.cancelWithPolicy(this, requestedDate, policy, context);
     }
 
     @Override
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 2ec89a3..3dd0927 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
@@ -71,7 +71,7 @@ public class TestUserApiAddOn extends TestApiBase {
             assertEquals(aoSubscription.getState(), SubscriptionState.ACTIVE);
 
             final DateTime now = clock.getUTCNow();
-            aoSubscription.cancel(now, false, context);
+            aoSubscription.cancel(now, context);
 
             testListener.reset();
             testListener.pushExpectedEvent(NextEvent.CANCEL);
@@ -119,7 +119,7 @@ public class TestUserApiAddOn extends TestApiBase {
             baseSubscription = (SubscriptionData) entitlementApi.getSubscriptionFromId(baseSubscription.getId());
 
             // FUTURE CANCELLATION
-            baseSubscription.cancel(now, false, context);
+            baseSubscription.cancel(now, context);
 
             // REFETCH AO SUBSCRIPTION AND CHECK THIS IS ACTIVE
             aoSubscription = (SubscriptionData) entitlementApi.getSubscriptionFromId(aoSubscription.getId());
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 8e70eee..6e3becc 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
@@ -64,7 +64,7 @@ public abstract class TestUserApiCancel extends TestApiBase {
 
 
             // CANCEL in trial period to get IMM policy
-            subscription.cancel(clock.getUTCNow(), false, context);
+            subscription.cancel(clock.getUTCNow(), context);
             currentPhase = subscription.getCurrentPhase();
             testListener.isCompleted(3000);
 
@@ -121,7 +121,7 @@ public abstract class TestUserApiCancel extends TestApiBase {
             // CANCEL
             testListener.setNonExpectedMode();
             testListener.pushExpectedEvent(NextEvent.CANCEL);
-            subscription.cancel(clock.getUTCNow(), false, context);
+            subscription.cancel(clock.getUTCNow(), context);
             assertFalse(testListener.isCompleted(3000));
             testListener.reset();
 
@@ -186,7 +186,7 @@ public abstract class TestUserApiCancel extends TestApiBase {
             testListener.pushExpectedEvent(NextEvent.CANCEL);
 
             // CANCEL
-            subscription.cancel(clock.getUTCNow(), false, context);
+            subscription.cancel(clock.getUTCNow(), context);
             assertTrue(testListener.isCompleted(5000));
 
             final PlanPhase currentPhase = subscription.getCurrentPhase();
@@ -232,7 +232,7 @@ public abstract class TestUserApiCancel extends TestApiBase {
             subscription = (SubscriptionData) entitlementApi.getSubscriptionFromId(subscription.getId());
 
             // CANCEL EOT
-            subscription.cancel(clock.getUTCNow(), false, context);
+            subscription.cancel(clock.getUTCNow(), context);
 
             subscription.uncancel(context);
 
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 dffd3de..29e8a87 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
@@ -159,7 +159,7 @@ public class TestUserApiDemos extends TestApiBase {
 
             /* STEP 8. CANCEL IMM (NO CTD) */
             testListener.pushExpectedEvent(NextEvent.CANCEL);
-            subscription.cancel(clock.getUTCNow(), false, context);
+            subscription.cancel(clock.getUTCNow(), context);
 
             displayState(subscription.getId(), "STEP 8.  CANCELLATION");
 
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 3beb9a9..f26bc35 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
@@ -146,7 +146,7 @@ public class TestUserApiError extends TestApiBase {
             final Subscription subscription = createSubscription("Shotgun", BillingPeriod.ANNUAL, PriceListSet.DEFAULT_PRICELIST_NAME);
 
             testListener.pushExpectedEvent(NextEvent.CANCEL);
-            subscription.cancel(clock.getUTCNow(), false, context);
+            subscription.cancel(clock.getUTCNow(), context);
             try {
                 subscription.changePlan("Pistol", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, clock.getUTCNow(), context);
             } catch (EntitlementUserApiException e) {
@@ -199,7 +199,7 @@ public class TestUserApiError extends TestApiBase {
 
             subscription = entitlementApi.getSubscriptionFromId(subscription.getId());
 
-            subscription.cancel(clock.getUTCNow(), false, context);
+            subscription.cancel(clock.getUTCNow(), context);
             try {
                 subscription.changePlan("Pistol", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, clock.getUTCNow(), context);
             } catch (EntitlementUserApiException e) {
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 d35c9f3..026f262 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
@@ -92,7 +92,7 @@ public abstract class TestUserApiRecreate extends TestApiBase {
 
         // NOW CANCEL ADN THIS SHOULD WORK
         testListener.pushExpectedEvent(NextEvent.CANCEL);
-        subscription.cancel(null, false, context);
+        subscription.cancel(null, context);
 
         testListener.pushExpectedEvent(NextEvent.PHASE);
         testListener.pushExpectedEvent(NextEvent.RE_CREATE);
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 e53c706..8859d69 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
@@ -71,7 +71,7 @@ public class TestUserApiScenarios extends TestApiBase {
             // CANCEL EOT
             testListener.setNonExpectedMode();
             testListener.pushExpectedEvent(NextEvent.CANCEL);
-            subscription.cancel(clock.getUTCNow(), false, context);
+            subscription.cancel(clock.getUTCNow(), context);
             assertFalse(testListener.isCompleted(5000));
             testListener.reset();
 
diff --git a/jaxrs/src/main/java/com/ning/billing/jaxrs/mappers/OverdueErrorMapper.java b/jaxrs/src/main/java/com/ning/billing/jaxrs/mappers/OverdueErrorMapper.java
index 98ac732..12c3a5f 100644
--- a/jaxrs/src/main/java/com/ning/billing/jaxrs/mappers/OverdueErrorMapper.java
+++ b/jaxrs/src/main/java/com/ning/billing/jaxrs/mappers/OverdueErrorMapper.java
@@ -23,11 +23,11 @@ import javax.ws.rs.core.UriInfo;
 import javax.ws.rs.ext.ExceptionMapper;
 import javax.ws.rs.ext.Provider;
 
-import com.ning.billing.overdue.config.api.OverdueError;
+import com.ning.billing.overdue.config.api.OverdueException;
 
 @Singleton
 @Provider
-public class OverdueErrorMapper extends ExceptionMapperBase implements ExceptionMapper<OverdueError> {
+public class OverdueErrorMapper extends ExceptionMapperBase implements ExceptionMapper<OverdueException> {
 
     private final UriInfo uriInfo;
 
@@ -36,7 +36,7 @@ public class OverdueErrorMapper extends ExceptionMapperBase implements Exception
     }
 
     @Override
-    public Response toResponse(final OverdueError exception) {
+    public Response toResponse(final OverdueException exception) {
         return buildBadRequestResponse(exception, uriInfo);
     }
 }
diff --git a/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/OverdueResource.java b/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/OverdueResource.java
index b4c6f64..3fcc1fc 100644
--- a/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/OverdueResource.java
+++ b/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/OverdueResource.java
@@ -36,7 +36,7 @@ import com.ning.billing.jaxrs.json.OverdueStateJson;
 import com.ning.billing.overdue.OverdueApiException;
 import com.ning.billing.overdue.OverdueState;
 import com.ning.billing.overdue.OverdueUserApi;
-import com.ning.billing.overdue.config.api.OverdueError;
+import com.ning.billing.overdue.config.api.OverdueException;
 
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
@@ -63,7 +63,7 @@ public class OverdueResource implements JaxrsResource {
     @GET
     @Path("/" + ACCOUNTS + "/{accountId:" + UUID_PATTERN + "}")
     @Produces(APPLICATION_JSON)
-    public Response getOverdueAccount(@PathParam("accountId") final String accountId) throws AccountApiException, OverdueError, OverdueApiException {
+    public Response getOverdueAccount(@PathParam("accountId") final String accountId) throws AccountApiException, OverdueException, OverdueApiException {
         final Account account = accountApi.getAccountById(UUID.fromString(accountId));
         final OverdueState<Account> overdueState = overdueApi.getOverdueStateFor(account);
 
@@ -73,7 +73,7 @@ public class OverdueResource implements JaxrsResource {
     @GET
     @Path("/" + BUNDLES + "/{bundleId:" + UUID_PATTERN + "}")
     @Produces(APPLICATION_JSON)
-    public Response getOverdueBundle(@PathParam("bundleId") final String bundleId) throws EntitlementUserApiException, OverdueError, OverdueApiException {
+    public Response getOverdueBundle(@PathParam("bundleId") final String bundleId) throws EntitlementUserApiException, OverdueException, OverdueApiException {
         final SubscriptionBundle bundle = entitlementApi.getBundleFromId(UUID.fromString(bundleId));
         final OverdueState<SubscriptionBundle> overdueState = overdueApi.getOverdueStateFor(bundle);
 
@@ -83,7 +83,7 @@ public class OverdueResource implements JaxrsResource {
     @GET
     @Path("/" + SUBSCRIPTIONS + "/{subscriptionId:" + UUID_PATTERN + "}")
     @Produces(APPLICATION_JSON)
-    public Response getOverdueSubscription(@PathParam("subscriptionId") final String subscriptionId) throws EntitlementUserApiException, OverdueError, OverdueApiException {
+    public Response getOverdueSubscription(@PathParam("subscriptionId") final String subscriptionId) throws EntitlementUserApiException, OverdueException, OverdueApiException {
         final Subscription subscription = entitlementApi.getSubscriptionFromId(UUID.fromString(subscriptionId));
         final OverdueState<Subscription> overdueState = overdueApi.getOverdueStateFor(subscription);
 
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 68dacad..178eea9 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
@@ -220,6 +220,7 @@ public class SubscriptionResource extends JaxRsResourceBase {
                                            @QueryParam(QUERY_REQUESTED_DT) final String requestedDate,
                                            @QueryParam(QUERY_CALL_COMPLETION) @DefaultValue("false") final Boolean callCompletion,
                                            @QueryParam(QUERY_CALL_TIMEOUT) @DefaultValue("5") final long timeoutSec,
+                                           @QueryParam(QUERY_POLICY) final String policyString,
                                            @HeaderParam(HDR_CREATED_BY) final String createdBy,
                                            @HeaderParam(HDR_REASON) final String reason,
                                            @HeaderParam(HDR_COMMENT) final String comment,
@@ -237,7 +238,12 @@ public class SubscriptionResource extends JaxRsResourceBase {
                 final Subscription current = entitlementApi.getSubscriptionFromId(uuid);
 
                 final DateTime inputDate = (requestedDate != null) ? DATE_TIME_FORMATTER.parseDateTime(requestedDate) : null;
-                isImmediateOp = current.cancel(inputDate, false, ctx);
+                if (policyString == null) {
+                    isImmediateOp = current.cancel(inputDate, ctx);
+                } else {
+                    final ActionPolicy policy = ActionPolicy.valueOf(policyString.toUpperCase());
+                    isImmediateOp = current.cancelWithPolicy(inputDate, policy, ctx);
+                }
                 return Response.status(Status.OK).build();
             }
 
diff --git a/junction/src/main/java/com/ning/billing/junction/plumbing/api/BlockingSubscription.java b/junction/src/main/java/com/ning/billing/junction/plumbing/api/BlockingSubscription.java
index ddc67e8..1bf3906 100644
--- a/junction/src/main/java/com/ning/billing/junction/plumbing/api/BlockingSubscription.java
+++ b/junction/src/main/java/com/ning/billing/junction/plumbing/api/BlockingSubscription.java
@@ -56,11 +56,18 @@ public class BlockingSubscription implements Subscription {
     }
 
     @Override
-    public boolean cancel(final DateTime requestedDate, final boolean eot, final CallContext context) throws EntitlementUserApiException {
-        return subscription.cancel(requestedDate, eot, context);
+    public boolean cancel(final DateTime requestedDate, final CallContext context) throws EntitlementUserApiException {
+        return subscription.cancel(requestedDate, context);
     }
 
     @Override
+    public boolean cancelWithPolicy(DateTime requestedDate, ActionPolicy policy, CallContext context)
+            throws EntitlementUserApiException {
+        return subscription.cancelWithPolicy(requestedDate, policy, context);
+    }
+
+
+    @Override
     public boolean uncancel(final CallContext context) throws EntitlementUserApiException {
         return subscription.uncancel(context);
     }
diff --git a/overdue/src/main/java/com/ning/billing/ovedue/notification/DefaultOverdueCheckNotifier.java b/overdue/src/main/java/com/ning/billing/ovedue/notification/DefaultOverdueCheckNotifier.java
index cf51e2b..c1fa16c 100644
--- a/overdue/src/main/java/com/ning/billing/ovedue/notification/DefaultOverdueCheckNotifier.java
+++ b/overdue/src/main/java/com/ning/billing/ovedue/notification/DefaultOverdueCheckNotifier.java
@@ -16,7 +16,6 @@
 
 package com.ning.billing.ovedue.notification;
 
-import java.util.UUID;
 
 import org.joda.time.DateTime;
 import org.slf4j.Logger;
@@ -77,7 +76,7 @@ public class DefaultOverdueCheckNotifier implements OverdueCheckNotifier {
                     }
 
                     final OverdueCheckNotificationKey key = (OverdueCheckNotificationKey) notificationKey;
-                    processEvent(key.getUuidKey(), eventDate);
+                    processEvent(key, eventDate);
                 } catch (IllegalArgumentException e) {
                     log.error("The key returned from the NextBillingNotificationQueue is not a valid UUID", e);
                 }
@@ -112,7 +111,7 @@ public class DefaultOverdueCheckNotifier implements OverdueCheckNotifier {
         }
     }
 
-    private void processEvent(final UUID overdueableId, final DateTime eventDateTime) {
-        listener.handleNextOverdueCheck(overdueableId);
+    private void processEvent(final OverdueCheckNotificationKey key, final DateTime eventDateTime) {
+        listener.handleNextOverdueCheck(key);
     }
 }
diff --git a/overdue/src/main/java/com/ning/billing/ovedue/notification/DefaultOverdueCheckPoster.java b/overdue/src/main/java/com/ning/billing/ovedue/notification/DefaultOverdueCheckPoster.java
index ad11522..5c90516 100644
--- a/overdue/src/main/java/com/ning/billing/ovedue/notification/DefaultOverdueCheckPoster.java
+++ b/overdue/src/main/java/com/ning/billing/ovedue/notification/DefaultOverdueCheckPoster.java
@@ -50,7 +50,7 @@ public class DefaultOverdueCheckPoster implements OverdueCheckPoster {
                                                                               DefaultOverdueCheckNotifier.OVERDUE_CHECK_NOTIFIER_QUEUE);
             log.info("Queuing overdue check notification. id: {}, timestamp: {}", overdueable.getId().toString(), futureNotificationTime.toString());
 
-            checkOverdueQueue.recordFutureNotification(futureNotificationTime, null, new OverdueCheckNotificationKey(overdueable.getId()));
+            checkOverdueQueue.recordFutureNotification(futureNotificationTime, null, new OverdueCheckNotificationKey(overdueable.getId(), Blockable.Type.get(overdueable)));
         } catch (NoSuchNotificationQueue e) {
             log.error("Attempting to put items on a non-existent queue (DefaultOverdueCheck).", e);
         } catch (IOException e) {
diff --git a/overdue/src/main/java/com/ning/billing/ovedue/notification/OverdueCheckNotificationKey.java b/overdue/src/main/java/com/ning/billing/ovedue/notification/OverdueCheckNotificationKey.java
index 3483e8d..70a72d0 100644
--- a/overdue/src/main/java/com/ning/billing/ovedue/notification/OverdueCheckNotificationKey.java
+++ b/overdue/src/main/java/com/ning/billing/ovedue/notification/OverdueCheckNotificationKey.java
@@ -20,11 +20,23 @@ import java.util.UUID;
 
 import com.fasterxml.jackson.annotation.JsonCreator;
 import com.fasterxml.jackson.annotation.JsonProperty;
+
+import com.ning.billing.junction.api.Blockable;
 import com.ning.billing.util.notificationq.DefaultUUIDNotificationKey;
 
 public class OverdueCheckNotificationKey extends DefaultUUIDNotificationKey {
+
+    private final Blockable.Type type;
+
     @JsonCreator
-    public OverdueCheckNotificationKey(@JsonProperty("uuidKey") UUID uuidKey) {
+    public OverdueCheckNotificationKey(@JsonProperty("uuidKey") UUID uuidKey,
+            @JsonProperty("type") Blockable.Type type) {
         super(uuidKey);
+        this.type = type;
+    }
+
+    // Hack : We default to SubscriptionBundle which is the only one supported at the time
+    public Blockable.Type getType() {
+        return type == null ? Blockable.Type.SUBSCRIPTION_BUNDLE : type;
     }
 }
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 c659083..3e5ffa5 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
@@ -30,7 +30,7 @@ import com.ning.billing.overdue.OverdueState;
 import com.ning.billing.overdue.OverdueUserApi;
 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.OverdueException;
 import com.ning.billing.overdue.config.api.OverdueStateSet;
 import com.ning.billing.overdue.wrapper.OverdueWrapper;
 import com.ning.billing.overdue.wrapper.OverdueWrapperFactory;
@@ -51,25 +51,25 @@ public class DefaultOverdueUserApi implements OverdueUserApi {
 
     @SuppressWarnings("unchecked")
     @Override
-    public <T extends Blockable> OverdueState<T> getOverdueStateFor(final T overdueable) throws OverdueError {
+    public <T extends Blockable> OverdueState<T> getOverdueStateFor(final T overdueable) throws OverdueException {
         try {
             final String stateName = accessApi.getBlockingStateFor(overdueable).getStateName();
             final OverdueStateSet<SubscriptionBundle> states = overdueConfig.getBundleStateSet();
             return (OverdueState<T>) states.findState(stateName);
         } catch (OverdueApiException e) {
-            throw new OverdueError(e, ErrorCode.OVERDUE_CAT_ERROR_ENCOUNTERED, overdueable.getId(), overdueable.getClass().getSimpleName());
+            throw new OverdueException(e, ErrorCode.OVERDUE_CAT_ERROR_ENCOUNTERED, overdueable.getId(), overdueable.getClass().getSimpleName());
         }
     }
 
     @Override
-    public <T extends Blockable> BillingState<T> getBillingStateFor(final T overdueable) throws OverdueError {
+    public <T extends Blockable> BillingState<T> getBillingStateFor(final T overdueable) throws OverdueException {
         log.info(String.format("Billing state of of %s requested", overdueable.getId()));
         final OverdueWrapper<T> wrapper = factory.createOverdueWrapperFor(overdueable);
         return wrapper.billingState();
     }
 
     @Override
-    public <T extends Blockable> OverdueState<T> refreshOverdueStateFor(final T blockable) throws OverdueError, OverdueApiException {
+    public <T extends Blockable> OverdueState<T> refreshOverdueStateFor(final T blockable) throws OverdueException, OverdueApiException {
         log.info(String.format("Refresh of %s requested", blockable.getId()));
         final 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 86c9a72..40a93bd 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
@@ -16,64 +16,105 @@
 
 package com.ning.billing.overdue.applicator;
 
+import java.util.LinkedList;
+import java.util.List;
+
 import org.joda.time.DateTime;
+import org.joda.time.LocalDate;
 import org.joda.time.Period;
 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.catalog.api.ActionPolicy;
+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.junction.api.Blockable;
 import com.ning.billing.junction.api.BlockingApi;
 import com.ning.billing.junction.api.BlockingApiException;
 import com.ning.billing.junction.api.DefaultBlockingState;
 import com.ning.billing.ovedue.notification.OverdueCheckPoster;
 import com.ning.billing.overdue.OverdueApiException;
+import com.ning.billing.overdue.OverdueCancellationPolicicy;
 import com.ning.billing.overdue.OverdueChangeEvent;
 import com.ning.billing.overdue.OverdueService;
 import com.ning.billing.overdue.OverdueState;
-import com.ning.billing.overdue.config.api.OverdueError;
+import com.ning.billing.overdue.config.api.BillingState;
+import com.ning.billing.overdue.config.api.OverdueException;
 import com.ning.billing.util.bus.Bus;
+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.clock.Clock;
 
 public class OverdueStateApplicator<T extends Blockable> {
+
+    private static final String API_USER_NAME = "OverdueStateApplicator";
+
     private static final Logger log = LoggerFactory.getLogger(OverdueStateApplicator.class);
 
     private final BlockingApi blockingApi;
     private final Clock clock;
     private final OverdueCheckPoster poster;
     private final Bus bus;
+    private final EntitlementUserApi entitlementUserApi;
+    private final CallContextFactory factory;
 
 
     @Inject
-    public OverdueStateApplicator(final BlockingApi accessApi, final Clock clock, final OverdueCheckPoster poster, final Bus bus) {
+    public OverdueStateApplicator(final BlockingApi accessApi, final EntitlementUserApi entitlementUserApi, final Clock clock,
+            final OverdueCheckPoster poster, final Bus bus, final CallContextFactory factory) {
         this.blockingApi = accessApi;
+        this.entitlementUserApi = entitlementUserApi;
         this.clock = clock;
         this.poster = poster;
         this.bus = bus;
+        this.factory = factory;
     }
 
-    public void apply(final T overdueable, final String previousOverdueStateName, final OverdueState<T> nextOverdueState) throws OverdueError {
-        if (previousOverdueStateName.equals(nextOverdueState.getName())) {
-            return; // nothing to do
-        }
 
-        storeNewState(overdueable, nextOverdueState);
+    public void apply(final OverdueState<T> firstOverdueState, final BillingState<T> billingState,
+            final T overdueable, final String previousOverdueStateName, final OverdueState<T> nextOverdueState) throws OverdueException {
+
         try {
+
+            // We did not reach first state, we we need to check if there is any pending condition for which we will not receive
+            // any notifications.. (last two conditions are there for test purpose)
+            if (nextOverdueState.isClearState() && firstOverdueState != null && billingState !=  null) {
+                 final LocalDate firstUnpaidInvoice = billingState.getDateOfEarliestUnpaidInvoice();
+                 if (firstUnpaidInvoice != null) {
+                     final Period reevaluationInterval = firstOverdueState.getReevaluationInterval();
+                     createFutureNotification(overdueable, firstUnpaidInvoice.toDateTimeAtCurrentTime().plus(reevaluationInterval));
+                 }
+            }
+
+            if (nextOverdueState == null || previousOverdueStateName.equals(nextOverdueState.getName())) {
+                return; //That's it we are done...
+            }
+
+            storeNewState(overdueable, nextOverdueState);
+
+            cancelSubscriptionsIfRequired(overdueable, nextOverdueState);
+
             final Period reevaluationInterval = nextOverdueState.getReevaluationInterval();
             if (!nextOverdueState.isClearState()) {
                 createFutureNotification(overdueable, clock.getUTCNow().plus(reevaluationInterval));
             }
         } catch (OverdueApiException e) {
             if (e.getCode() != ErrorCode.OVERDUE_NO_REEVALUATION_INTERVAL.getCode()) {
-                throw new OverdueError(e);
+                throw new OverdueException(e);
             }
         }
 
         if (nextOverdueState.isClearState()) {
             clear(overdueable);
         }
-        
+
         try {
             bus.post(createOverdueEvent(overdueable, previousOverdueStateName, nextOverdueState.getName()));
         } catch (Exception e) {
@@ -86,12 +127,12 @@ public class OverdueStateApplicator<T extends Blockable> {
         return new DefaultOverdueChangeEvent(overdueable.getId(), Blockable.Type.get(overdueable), previousOverdueStateName, nextOverdueStateName, null);
     }
 
-    protected void storeNewState(final T blockable, final OverdueState<T> nextOverdueState) throws OverdueError {
+    protected void storeNewState(final T blockable, final OverdueState<T> nextOverdueState) throws OverdueException {
         try {
             blockingApi.setBlockingState(new DefaultBlockingState(blockable.getId(), nextOverdueState.getName(), Blockable.Type.get(blockable),
                                                                   OverdueService.OVERDUE_SERVICE_NAME, blockChanges(nextOverdueState), blockEntitlement(nextOverdueState), blockBilling(nextOverdueState)));
         } catch (Exception e) {
-            throw new OverdueError(e, ErrorCode.OVERDUE_CAT_ERROR_ENCOUNTERED, blockable.getId(), blockable.getClass().getName());
+            throw new OverdueException(e, ErrorCode.OVERDUE_CAT_ERROR_ENCOUNTERED, blockable.getId(), blockable.getClass().getName());
         }
     }
 
@@ -118,4 +159,46 @@ public class OverdueStateApplicator<T extends Blockable> {
         poster.clearNotificationsFor(blockable);
     }
 
+    private void cancelSubscriptionsIfRequired(final T blockable, final OverdueState<T> nextOverdueState) throws OverdueException {
+        if (nextOverdueState.getSubscriptionCancellationPolicy() == OverdueCancellationPolicicy.NONE) {
+            return;
+        }
+        try {
+            ActionPolicy actionPolicy = null;
+            switch(nextOverdueState.getSubscriptionCancellationPolicy()) {
+            case END_OF_TERM:
+                actionPolicy = ActionPolicy.END_OF_TERM;
+                break;
+            case IMMEDIATE:
+                actionPolicy = ActionPolicy.IMMEDIATE;
+                break;
+            default :
+                throw new IllegalStateException("Unexpected OverdueCancellationPolicy " + nextOverdueState.getSubscriptionCancellationPolicy());
+            }
+            final List<Subscription> toBeCancelled = new LinkedList<Subscription>();
+            computeSubscriptionsToCancel(blockable, toBeCancelled);
+            final CallContext context = factory.createCallContext(API_USER_NAME, CallOrigin.INTERNAL, UserType.SYSTEM);
+            for (Subscription cur : toBeCancelled) {
+                cur.cancelWithPolicy(clock.getUTCNow(), actionPolicy, context);
+            }
+        } catch (EntitlementUserApiException e) {
+            throw new OverdueException(e);
+        }
+    }
+
+    @SuppressWarnings("unchecked")
+    private void computeSubscriptionsToCancel(final T blockable, final List<Subscription> result) throws EntitlementUserApiException{
+        if (blockable instanceof Subscription) {
+            result.add((Subscription) blockable);
+            return;
+        } else if (blockable instanceof SubscriptionBundle) {
+            for (Subscription cur : entitlementUserApi.getSubscriptionsForBundle(blockable.getId())) {
+                computeSubscriptionsToCancel((T) cur, result);
+            }
+        } else if (blockable instanceof Account) {
+            for (SubscriptionBundle cur : entitlementUserApi.getBundlesForAccount(blockable.getId())) {
+                computeSubscriptionsToCancel((T) cur, result);
+            }
+        }
+    }
 }
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 8346357..feb0bf4 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
@@ -31,7 +31,7 @@ import com.ning.billing.invoice.api.Invoice;
 import com.ning.billing.invoice.api.InvoiceUserApi;
 import com.ning.billing.junction.api.Blockable;
 import com.ning.billing.overdue.config.api.BillingState;
-import com.ning.billing.overdue.config.api.OverdueError;
+import com.ning.billing.overdue.config.api.OverdueException;
 import com.ning.billing.util.clock.Clock;
 
 import com.google.inject.Inject;
@@ -60,7 +60,7 @@ public abstract class BillingStateCalculator<T extends Blockable> {
         this.clock = clock;
     }
 
-    public abstract BillingState<T> calculateBillingState(T overdueable) throws OverdueError;
+    public abstract BillingState<T> calculateBillingState(T overdueable) throws OverdueException;
 
     protected Invoice earliest(final SortedSet<Invoice> unpaidInvoices) {
         try {
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 a72ca90..3a5b607 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
@@ -42,7 +42,7 @@ import com.ning.billing.invoice.api.Invoice;
 import com.ning.billing.invoice.api.InvoiceItem;
 import com.ning.billing.invoice.api.InvoiceUserApi;
 import com.ning.billing.overdue.config.api.BillingStateBundle;
-import com.ning.billing.overdue.config.api.OverdueError;
+import com.ning.billing.overdue.config.api.OverdueException;
 import com.ning.billing.overdue.config.api.PaymentResponse;
 import com.ning.billing.util.clock.Clock;
 import com.ning.billing.util.tag.Tag;
@@ -61,7 +61,7 @@ public class BillingStateCalculatorBundle extends BillingStateCalculator<Subscri
     }
 
     @Override
-    public BillingStateBundle calculateBillingState(final SubscriptionBundle bundle) throws OverdueError {
+    public BillingStateBundle calculateBillingState(final SubscriptionBundle bundle) throws OverdueException {
         try {
             final Account account = accountApi.getAccountById(bundle.getAccountId());
             final SortedSet<Invoice> unpaidInvoices = unpaidInvoicesForBundle(bundle.getId(), bundle.getAccountId(), account.getTimeZone());
@@ -111,9 +111,9 @@ public class BillingStateCalculatorBundle extends BillingStateCalculator<Subscri
                                           basePlanPriceList,
                                           basePlanPhaseType);
         } catch (EntitlementUserApiException e) {
-            throw new OverdueError(e);
+            throw new OverdueException(e);
         } catch (AccountApiException e) {
-            throw new OverdueError(e);
+            throw new OverdueException(e);
         }
     }
 
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 cd50142..359beaa 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
@@ -24,7 +24,9 @@ import javax.xml.bind.annotation.XmlAccessorType;
 import javax.xml.bind.annotation.XmlElement;
 import javax.xml.bind.annotation.XmlElementWrapper;
 
+import org.joda.time.DateTime;
 import org.joda.time.LocalDate;
+import org.joda.time.Period;
 
 import com.ning.billing.catalog.api.Duration;
 import com.ning.billing.catalog.api.TimeUnit;
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 cc4e859..383b3fd 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
@@ -28,6 +28,7 @@ import com.ning.billing.ErrorCode;
 import com.ning.billing.catalog.api.TimeUnit;
 import com.ning.billing.junction.api.Blockable;
 import com.ning.billing.overdue.OverdueApiException;
+import com.ning.billing.overdue.OverdueCancellationPolicicy;
 import com.ning.billing.overdue.OverdueState;
 import com.ning.billing.util.config.ValidatingConfig;
 import com.ning.billing.util.config.ValidationError;
@@ -38,6 +39,7 @@ public class DefaultOverdueState<T extends Blockable> extends ValidatingConfig<O
 
     private static final int MAX_NAME_LENGTH = 50;
 
+
     @XmlElement(required = false, name = "condition")
     private DefaultCondition<T> condition;
 
@@ -54,6 +56,9 @@ public class DefaultOverdueState<T extends Blockable> extends ValidatingConfig<O
     @XmlElement(required = false, name = "disableEntitlementAndChangesBlocked")
     private Boolean disableEntitlement = false;
 
+    @XmlElement(required = false, name = "subscriptionCancellationPolicy")
+    private OverdueCancellationPolicicy subscriptionCancellationPolicy = OverdueCancellationPolicicy.NONE;
+
     @XmlElement(required = false, name = "isClearState")
     private Boolean isClearState = false;
 
@@ -99,6 +104,11 @@ public class DefaultOverdueState<T extends Blockable> extends ValidatingConfig<O
     }
 
     @Override
+    public OverdueCancellationPolicicy getSubscriptionCancellationPolicy() {
+        return subscriptionCancellationPolicy;
+    }
+
+    @Override
     public Period getReevaluationInterval() throws OverdueApiException {
         if (autoReevaluationInterval == null || autoReevaluationInterval.getUnit() == TimeUnit.UNLIMITED || autoReevaluationInterval.getNumber() == 0) {
             throw new OverdueApiException(ErrorCode.OVERDUE_NO_REEVALUATION_INTERVAL, name);
@@ -106,6 +116,7 @@ public class DefaultOverdueState<T extends Blockable> extends ValidatingConfig<O
         return autoReevaluationInterval.toJodaPeriod();
     }
 
+    @Override
     public DefaultCondition<T> getCondition() {
         return condition;
     }
@@ -130,6 +141,12 @@ public class DefaultOverdueState<T extends Blockable> extends ValidatingConfig<O
         return this;
     }
 
+    public DefaultOverdueState<T> setSubscriptionCancellationPolicy(final OverdueCancellationPolicicy policy) {
+        this.subscriptionCancellationPolicy = policy;
+        return this;
+    }
+
+
     protected DefaultOverdueState<T> setBlockChanges(final boolean cancel) {
         this.blockChanges = cancel;
         return this;
@@ -159,6 +176,4 @@ public class DefaultOverdueState<T extends Blockable> extends ValidatingConfig<O
         final Integer daysBetweenPaymentRetries = 8;
         return daysBetweenPaymentRetries;
     }
-
-
 }
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 8b3419a..263a8e1 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
@@ -19,7 +19,6 @@ package com.ning.billing.overdue.config;
 import javax.xml.bind.annotation.XmlAccessType;
 import javax.xml.bind.annotation.XmlAccessorType;
 
-import org.joda.time.DateTime;
 import org.joda.time.LocalDate;
 import org.joda.time.Period;
 
@@ -95,4 +94,8 @@ public abstract class DefaultOverdueStateSet<T extends Blockable> extends Valida
         return getStates().length;
     }
 
+    @Override
+    public OverdueState<T> getFirstState() {
+        return getStates()[0];
+    }
 }
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 a94448f..e7dcda8 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
@@ -28,7 +28,7 @@ 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.overdue.OverdueApiException;
-import com.ning.billing.overdue.config.api.OverdueError;
+import com.ning.billing.overdue.config.api.OverdueException;
 import com.ning.billing.overdue.wrapper.OverdueWrapperFactory;
 
 public class OverdueDispatcher {
@@ -64,17 +64,17 @@ public class OverdueDispatcher {
     public void processOverdue(final Blockable blockable) {
         try {
             factory.createOverdueWrapperFor(blockable).refresh();
-        } catch (OverdueError e) {
+        } catch (OverdueException 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: " + blockable.getId().toString(), e);
         }
     }
 
-    public void processOverdue(final UUID blockableId) {
+    public void processOverdue(final Blockable.Type type, final UUID blockableId) {
         try {
-            factory.createOverdueWrapperFor(blockableId).refresh();
-        } catch (OverdueError e) {
+            factory.createOverdueWrapperFor(type, blockableId).refresh();
+        } catch (OverdueException 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 b4688b5..b9c7c89 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
@@ -22,6 +22,7 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import com.ning.billing.invoice.api.InvoiceAdjustmentEvent;
+import com.ning.billing.ovedue.notification.OverdueCheckNotificationKey;
 import com.ning.billing.payment.api.PaymentErrorEvent;
 import com.ning.billing.payment.api.PaymentInfoEvent;
 
@@ -59,8 +60,9 @@ public class OverdueListener {
         dispatcher.processOverdueForAccount(accountId);
     }
 
-    public void handleNextOverdueCheck(final UUID overdueableId) {
-        log.info(String.format("Received OD checkup notification for %s", overdueableId));
-        dispatcher.processOverdue(overdueableId);
+    public void handleNextOverdueCheck(final OverdueCheckNotificationKey notificationKey) {
+        log.info(String.format("Received OD checkup notification for type = %s, id = %s",
+                notificationKey.getType(), notificationKey.getUuidKey()));
+        dispatcher.processOverdue(notificationKey.getType(), notificationKey.getUuidKey());
     }
 }
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 5ed1d52..e7330ac 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
@@ -23,7 +23,7 @@ import com.ning.billing.overdue.OverdueState;
 import com.ning.billing.overdue.applicator.OverdueStateApplicator;
 import com.ning.billing.overdue.calculator.BillingStateCalculator;
 import com.ning.billing.overdue.config.api.BillingState;
-import com.ning.billing.overdue.config.api.OverdueError;
+import com.ning.billing.overdue.config.api.OverdueException;
 import com.ning.billing.overdue.config.api.OverdueStateSet;
 import com.ning.billing.util.clock.Clock;
 
@@ -48,7 +48,7 @@ public class OverdueWrapper<T extends Blockable> {
         this.overdueStateApplicator = overdueStateApplicator;
     }
 
-    public OverdueState<T> refresh() throws OverdueError, OverdueApiException {
+    public OverdueState<T> refresh() throws OverdueException, OverdueApiException {
         if (overdueStateSet.size() < 1) { // No configuration available
             return overdueStateSet.getClearState();
         }
@@ -57,14 +57,12 @@ public class OverdueWrapper<T extends Blockable> {
         final String previousOverdueStateName = api.getBlockingStateFor(overdueable).getStateName();
         final OverdueState<T> nextOverdueState = overdueStateSet.calculateOverdueState(billingState, clock.getToday(billingState.getAccountTimeZone()));
 
-        if (nextOverdueState != null && !previousOverdueStateName.equals(nextOverdueState.getName())) {
-            overdueStateApplicator.apply(overdueable, previousOverdueStateName, nextOverdueState);
-        }
+        overdueStateApplicator.apply(overdueStateSet.getFirstState(), billingState, overdueable, previousOverdueStateName, nextOverdueState);
 
         return nextOverdueState;
     }
 
-    public BillingState<T> billingState() throws OverdueError {
+    public BillingState<T> billingState() throws OverdueException {
         return billingStateCalcuator.calculateBillingState(overdueable);
     }
 }
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 aed2aad..c410e28 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
@@ -34,7 +34,7 @@ import com.ning.billing.overdue.calculator.BillingStateCalculatorBundle;
 import com.ning.billing.overdue.config.DefaultOverdueState;
 import com.ning.billing.overdue.config.DefaultOverdueStateSet;
 import com.ning.billing.overdue.config.OverdueConfig;
-import com.ning.billing.overdue.config.api.OverdueError;
+import com.ning.billing.overdue.config.api.OverdueException;
 import com.ning.billing.overdue.config.api.OverdueStateSet;
 import com.ning.billing.util.clock.Clock;
 
@@ -61,34 +61,32 @@ public class OverdueWrapperFactory {
     }
 
     @SuppressWarnings("unchecked")
-    public <T extends Blockable> OverdueWrapper<T> createOverdueWrapperFor(final T bloackable) throws OverdueError {
+    public <T extends Blockable> OverdueWrapper<T> createOverdueWrapperFor(final T bloackable) throws OverdueException {
 
         if (bloackable instanceof SubscriptionBundle) {
             return (OverdueWrapper<T>) new OverdueWrapper<SubscriptionBundle>((SubscriptionBundle) bloackable, api, getOverdueStateSetBundle(),
                                                                               clock, billingStateCalcuatorBundle, overdueStateApplicatorBundle);
         } else {
-            throw new OverdueError(ErrorCode.OVERDUE_TYPE_NOT_SUPPORTED, bloackable.getId(), bloackable.getClass());
+            throw new OverdueException(ErrorCode.OVERDUE_TYPE_NOT_SUPPORTED, bloackable.getId(), bloackable.getClass());
         }
     }
 
     @SuppressWarnings("unchecked")
-    public <T extends Blockable> OverdueWrapper<T> createOverdueWrapperFor(final UUID id) throws OverdueError {
-        final BlockingState state = api.getBlockingStateFor(id);
-
+    public <T extends Blockable> OverdueWrapper<T> createOverdueWrapperFor(final Blockable.Type type, final UUID id) throws OverdueException {
         try {
-            switch (state.getType()) {
+            switch (type) {
                 case SUBSCRIPTION_BUNDLE: {
                     final SubscriptionBundle bundle = entitlementApi.getBundleFromId(id);
                     return (OverdueWrapper<T>) new OverdueWrapper<SubscriptionBundle>(bundle, api, getOverdueStateSetBundle(),
                                                                                       clock, billingStateCalcuatorBundle, overdueStateApplicatorBundle);
                 }
                 default: {
-                    throw new OverdueError(ErrorCode.OVERDUE_TYPE_NOT_SUPPORTED, id, state.getType());
+                    throw new OverdueException(ErrorCode.OVERDUE_TYPE_NOT_SUPPORTED, id, type);
                 }
 
             }
         } catch (EntitlementUserApiException e) {
-            throw new OverdueError(e);
+            throw new OverdueException(e);
         }
     }
 
diff --git a/overdue/src/test/java/com/ning/billing/overdue/applicator/TestOverdueStateApplicator.java b/overdue/src/test/java/com/ning/billing/overdue/applicator/TestOverdueStateApplicator.java
index 87347a3..067b2c1 100644
--- a/overdue/src/test/java/com/ning/billing/overdue/applicator/TestOverdueStateApplicator.java
+++ b/overdue/src/test/java/com/ning/billing/overdue/applicator/TestOverdueStateApplicator.java
@@ -63,17 +63,17 @@ public class TestOverdueStateApplicator extends OverdueTestBase {
         OverdueState<SubscriptionBundle> state;
 
         state = config.getBundleStateSet().findState("OD1");
-        applicator.apply(bundle, BlockingApi.CLEAR_STATE_NAME, state);
+        applicator.apply(null, null, bundle, BlockingApi.CLEAR_STATE_NAME, state);
         checkStateApplied(state);
         checkBussEvent("OD1");
 
         state = config.getBundleStateSet().findState("OD2");
-        applicator.apply(bundle, BlockingApi.CLEAR_STATE_NAME, state);
+        applicator.apply(null, null,bundle, BlockingApi.CLEAR_STATE_NAME, state);
         checkStateApplied(state);
         checkBussEvent("OD2");
 
         state = config.getBundleStateSet().findState("OD3");
-        applicator.apply(bundle, BlockingApi.CLEAR_STATE_NAME, state);
+        applicator.apply(null, null, bundle, BlockingApi.CLEAR_STATE_NAME, state);
         checkStateApplied(state);
         checkBussEvent("OD3");
         bus.stop();
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 5965e02..d2cecf1 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
@@ -50,6 +50,7 @@ import com.ning.billing.mock.glue.MockJunctionModule;
 import com.ning.billing.mock.glue.MockPaymentModule;
 import com.ning.billing.ovedue.notification.DefaultOverdueCheckNotifier;
 import com.ning.billing.ovedue.notification.DefaultOverdueCheckPoster;
+import com.ning.billing.ovedue.notification.OverdueCheckNotificationKey;
 import com.ning.billing.ovedue.notification.OverdueCheckPoster;
 import com.ning.billing.overdue.OverdueProperties;
 import com.ning.billing.overdue.OverdueTestSuiteWithEmbeddedDB;
@@ -92,9 +93,9 @@ public class TestOverdueCheckNotifier extends OverdueTestSuiteWithEmbeddedDB {
         }
 
         @Override
-        public void handleNextOverdueCheck(final UUID subscriptionId) {
+        public void handleNextOverdueCheck(final OverdueCheckNotificationKey key) {
             eventCount++;
-            latestSubscriptionId = subscriptionId;
+            latestSubscriptionId = key.getUuidKey();
         }
 
         public int getEventCount() {
@@ -109,6 +110,7 @@ public class TestOverdueCheckNotifier extends OverdueTestSuiteWithEmbeddedDB {
     @BeforeClass(groups = "slow")
     public void setup() throws ServiceException, IOException, ClassNotFoundException, SQLException, EntitlementUserApiException {
         final Injector g = Guice.createInjector(Stage.PRODUCTION, new MockInvoiceModule(), new MockPaymentModule(), new BusModule(), new DefaultOverdueModule() {
+            @Override
             protected void configure() {
                 super.configure();
                 bind(Clock.class).to(ClockMock.class).asEagerSingleton();
diff --git a/overdue/src/test/java/com/ning/billing/overdue/notification/TestOverdueNotificationKeyJson.java b/overdue/src/test/java/com/ning/billing/overdue/notification/TestOverdueNotificationKeyJson.java
new file mode 100644
index 0000000..183ffae
--- /dev/null
+++ b/overdue/src/test/java/com/ning/billing/overdue/notification/TestOverdueNotificationKeyJson.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.notification;
+
+import static org.testng.Assert.assertEquals;
+
+import java.util.UUID;
+
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+import com.ning.billing.junction.api.Blockable;
+import com.ning.billing.ovedue.notification.OverdueCheckNotificationKey;
+import com.ning.billing.util.jackson.ObjectMapper;
+
+public class TestOverdueNotificationKeyJson {
+
+
+    private final ObjectMapper mapper = new ObjectMapper();
+
+    @Test(groups = "fast")
+    public void testOverdueNotificationKeyJson() throws Exception {
+
+        final UUID uuid = UUID.randomUUID();
+        final Blockable.Type type  = Blockable.Type.SUBSCRIPTION;
+
+        final OverdueCheckNotificationKey e = new OverdueCheckNotificationKey(uuid, type);
+
+        final String json = mapper.writeValueAsString(e);
+
+        final Class<?> claz = Class.forName(OverdueCheckNotificationKey.class.getName());
+        final Object obj = mapper.readValue(json, claz);
+        Assert.assertTrue(obj.equals(e));
+    }
+
+    @Test(groups = "fast")
+    public void testOverdueNotificationKeyJsonWithNoKey() throws Exception {
+
+        final String uuidString = "bab0fca4-c628-4997-8980-14d6c3a98c48";
+        final String json = "{\"uuidKey\":\"" + uuidString +  "\"}";
+
+        final Class<?> claz = Class.forName(OverdueCheckNotificationKey.class.getName());
+        final OverdueCheckNotificationKey obj = (OverdueCheckNotificationKey) mapper.readValue(json, claz);
+        assertEquals(obj.getUuidKey().toString(), uuidString);
+        assertEquals(obj.getType(), Blockable.Type.SUBSCRIPTION_BUNDLE);
+    }
+}
diff --git a/overdue/src/test/java/com/ning/billing/overdue/OverdueTestBase.java b/overdue/src/test/java/com/ning/billing/overdue/OverdueTestBase.java
index 45a5626..6b8d67e 100644
--- a/overdue/src/test/java/com/ning/billing/overdue/OverdueTestBase.java
+++ b/overdue/src/test/java/com/ning/billing/overdue/OverdueTestBase.java
@@ -60,10 +60,11 @@ import com.ning.billing.overdue.service.DefaultOverdueService;
 import com.ning.billing.overdue.wrapper.OverdueWrapperFactory;
 import com.ning.billing.util.bus.BusService;
 import com.ning.billing.util.clock.ClockMock;
+import com.ning.billing.util.glue.CallContextModule;
 import com.ning.billing.util.glue.NotificationQueueModule;
 import com.ning.billing.util.notificationq.NotificationQueueService.NotificationQueueAlreadyExists;
 
-@Guice(modules = {DefaultOverdueModule.class, OverdueListenerTesterModule.class, MockClockModule.class, ApplicatorMockJunctionModule.class, CatalogModule.class, MockInvoiceModule.class, MockPaymentModule.class, NotificationQueueModule.class, TestDbiModule.class})
+@Guice(modules = {DefaultOverdueModule.class, OverdueListenerTesterModule.class, MockClockModule.class, ApplicatorMockJunctionModule.class, CallContextModule.class, CatalogModule.class, MockInvoiceModule.class, MockPaymentModule.class, NotificationQueueModule.class, TestDbiModule.class})
 public abstract class OverdueTestBase extends OverdueTestSuiteWithEmbeddedDB {
     protected final String configXml =
             "<overdueConfig>" +
diff --git a/util/src/test/java/com/ning/billing/mock/MockSubscription.java b/util/src/test/java/com/ning/billing/mock/MockSubscription.java
index 1425500..0ff6407 100644
--- a/util/src/test/java/com/ning/billing/mock/MockSubscription.java
+++ b/util/src/test/java/com/ning/billing/mock/MockSubscription.java
@@ -70,8 +70,15 @@ public class MockSubscription implements Subscription {
     Subscription sub = Mockito.mock(Subscription.class);
 
     @Override
-    public boolean cancel(final DateTime requestedDate, final boolean eot, final CallContext context) throws EntitlementUserApiException {
-        return sub.cancel(requestedDate, eot, context);
+    public boolean cancel(final DateTime requestedDate, final CallContext context) throws EntitlementUserApiException {
+        return sub.cancel(requestedDate, context);
+    }
+
+    @Override
+    public boolean cancelWithPolicy(DateTime requestedDate,
+            ActionPolicy policy, CallContext context)
+            throws EntitlementUserApiException {
+        return sub.cancelWithPolicy(requestedDate, policy, context);
     }
 
     @Override