killbill-aplcache

Merge remote-tracking branch 'origin/work-for-release-0.16.2'

2/2/2016 7:49:38 PM

Changes

Details

diff --git a/api/src/main/java/org/killbill/billing/junction/DefaultBlockingState.java b/api/src/main/java/org/killbill/billing/junction/DefaultBlockingState.java
index b548665..07537b0 100644
--- a/api/src/main/java/org/killbill/billing/junction/DefaultBlockingState.java
+++ b/api/src/main/java/org/killbill/billing/junction/DefaultBlockingState.java
@@ -29,9 +29,6 @@ import org.killbill.clock.Clock;
 
 public class DefaultBlockingState extends EntityBase implements BlockingState {
 
-    public static final String CLEAR_STATE_NAME = "__KILLBILL__CLEAR__OVERDUE_STATE__";
-
-    private static BlockingState clearState = null;
 
     private final UUID blockedId;
     private final String stateName;
@@ -43,13 +40,6 @@ public class DefaultBlockingState extends EntityBase implements BlockingState {
     private final BlockingStateType type;
     private final Long totalOrdering;
 
-    public static BlockingState getClearState(final BlockingStateType type, final String serviceName, final Clock clock) {
-        if (clearState == null) {
-            clearState = new DefaultBlockingState(null, type, CLEAR_STATE_NAME, serviceName, false, false, false, clock.getUTCNow());
-        }
-        return clearState;
-    }
-
     // Used by the DAO
     public DefaultBlockingState(final UUID id,
                                 final UUID blockedId,
diff --git a/api/src/main/java/org/killbill/billing/util/UUIDs.java b/api/src/main/java/org/killbill/billing/util/UUIDs.java
index 9a89bdd..c1fc204 100644
--- a/api/src/main/java/org/killbill/billing/util/UUIDs.java
+++ b/api/src/main/java/org/killbill/billing/util/UUIDs.java
@@ -1,5 +1,6 @@
 /*
- * Copyright 2015 The Billing Project, LLC
+ * Copyright 2015-2016 Groupon, Inc
+ * Copyright 2015-2016 The Billing Project, LLC
  *
  * The Billing Project licenses this file to you under the Apache License, version 2.0
  * (the "License"); you may not use this file except in compliance with the
@@ -30,6 +31,14 @@ public abstract class UUIDs {
 
     public static UUID randomUUID() { return rndUUIDv4(); }
 
+    public static void setRandom(final Random random) {
+        threadRandom.set(random);
+    }
+
+    public static Random getRandom() {
+        return threadRandom.get();
+    }
+
     private static UUID rndUUIDv4() {
         // ~ return UUID.randomUUID() :
         final Random random = threadRandom.get();
diff --git a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/overdue/TestBillingAlignment.java b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/overdue/TestBillingAlignment.java
index 086fab5..9ee5303 100644
--- a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/overdue/TestBillingAlignment.java
+++ b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/overdue/TestBillingAlignment.java
@@ -21,8 +21,6 @@ package org.killbill.billing.beatrix.integration.overdue;
 import java.math.BigDecimal;
 
 import org.joda.time.LocalDate;
-import org.testng.annotations.Test;
-
 import org.killbill.billing.account.api.Account;
 import org.killbill.billing.api.TestApiListener.NextEvent;
 import org.killbill.billing.beatrix.integration.TestIntegrationBase;
@@ -31,22 +29,18 @@ import org.killbill.billing.catalog.api.BillingPeriod;
 import org.killbill.billing.catalog.api.ProductCategory;
 import org.killbill.billing.entitlement.api.DefaultEntitlement;
 import org.killbill.billing.invoice.api.InvoiceItemType;
+import org.killbill.billing.subscription.api.user.SubscriptionBaseTransition;
+import org.testng.Assert;
+import org.testng.annotations.Test;
 
 import static org.testng.Assert.assertNotNull;
 
 public class TestBillingAlignment extends TestIntegrationBase {
 
-    // TODO test fails as it should not create a proration when the chnage to annual occurs. Instaed we should restart from the data of the chnage
-    // since we have as a catalog rule:
-    // <billingAlignmentCase>
-    // <billingPeriod>ANNUAL</billingPeriod>
-    // <alignment>SUBSCRIPTION</alignment>
-    // </billingAlignmentCase>
-    //
-    @Test(groups = "slow", enabled = false)
-    public void testTransitonAccountBAToSubscriptionBA() throws Exception {
-
-        final Account account = createAccountWithNonOsgiPaymentMethod(getAccountData(1));
+    @Test(groups = "slow")
+    public void testTransitionAccountBAToSubscriptionBA() throws Exception {
+        // Set the BCD to the 25th
+        final Account account = createAccountWithNonOsgiPaymentMethod(getAccountData(25));
 
         // We take april as it has 30 days (easier to play with BCD)
         // Set clock to the initial start date - we implicitly assume here that the account timezone is UTC
@@ -54,17 +48,44 @@ public class TestBillingAlignment extends TestIntegrationBase {
 
         //
         // CREATE SUBSCRIPTION AND EXPECT BOTH EVENTS: NextEvent.CREATE NextEvent.INVOICE
-        // (Start with monthly that has a 'Account' billing alignment
+        // (Start with monthly that has an 'Account' billing alignment)
         //
         final DefaultEntitlement bpEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "externalKey", "Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, NextEvent.CREATE, NextEvent.INVOICE);
         assertNotNull(bpEntitlement);
+
         invoiceChecker.checkInvoice(account.getId(), 1, callContext, new ExpectedInvoiceItemCheck(new LocalDate(2012, 4, 1), null, InvoiceItemType.FIXED, new BigDecimal("0")));
+        invoiceChecker.checkChargedThroughDate(bpEntitlement.getId(), new LocalDate(2012, 4, 1), callContext);
 
-        // GET OUT TRIAL
+        // GET OUT TRIAL (moving clock to 2012-05-04)
         addDaysAndCheckForCompletion(33, NextEvent.PHASE, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
 
-        //
-        // Change plan to annual that has been configured to have a 'SubscriptionBase' billing alignment
-        changeEntitlementAndCheckForCompletion(bpEntitlement, "Shotgun", BillingPeriod.ANNUAL, null, NextEvent.CHANGE, NextEvent.INVOICE);
+        invoiceChecker.checkInvoice(account.getId(), 2, callContext, new ExpectedInvoiceItemCheck(new LocalDate(2012, 5, 1), new LocalDate(2012, 5, 25), InvoiceItemType.RECURRING, new BigDecimal("199.96")));
+        invoiceChecker.checkChargedThroughDate(bpEntitlement.getId(), new LocalDate(2012, 5, 25), callContext);
+
+        // Change plan to annual that has been configured to have a 'Subscription' billing alignment
+        final DefaultEntitlement changedBpEntitlement = changeEntitlementAndCheckForCompletion(bpEntitlement, "Shotgun", BillingPeriod.ANNUAL, null, NextEvent.CHANGE, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
+
+        invoiceChecker.checkInvoice(account.getId(),
+                                    3,
+                                    callContext,
+                                    new ExpectedInvoiceItemCheck(new LocalDate(2012, 5, 4), new LocalDate(2013, 5, 1), InvoiceItemType.RECURRING, new BigDecimal("2380.22")),
+                                    new ExpectedInvoiceItemCheck(new LocalDate(2012, 5, 4), new LocalDate(2012, 5, 25), InvoiceItemType.REPAIR_ADJ, new BigDecimal("-174.97")));
+        invoiceChecker.checkChargedThroughDate(bpEntitlement.getId(), new LocalDate(2013, 5, 1), callContext);
+
+        Assert.assertEquals(changedBpEntitlement.getSubscriptionBase().getAllTransitions().size(), 3);
+
+        final SubscriptionBaseTransition trial = changedBpEntitlement.getSubscriptionBase().getAllTransitions().get(0);
+        Assert.assertEquals(trial.getEffectiveTransitionTime().toLocalDate().compareTo(new LocalDate(2012, 4, 1)), 0);
+        Assert.assertEquals(trial.getNextPhase().getName(), "shotgun-monthly-trial");
+
+        final SubscriptionBaseTransition smEvergreen = changedBpEntitlement.getSubscriptionBase().getAllTransitions().get(1);
+        Assert.assertEquals(smEvergreen.getEffectiveTransitionTime().toLocalDate().compareTo(new LocalDate(2012, 5, 1)), 0);
+        Assert.assertEquals(smEvergreen.getNextPhase().getName(), "shotgun-monthly-evergreen");
+
+        final SubscriptionBaseTransition saEvergreen = changedBpEntitlement.getSubscriptionBase().getAllTransitions().get(2);
+        // Verify the IMMEDIATE policy
+        Assert.assertEquals(saEvergreen.getEffectiveTransitionTime().toLocalDate().compareTo(new LocalDate(2012, 5, 4)), 0);
+        // Verify the START_OF_SUBSCRIPTION alignment (both plans have the same 30 days trial)
+        Assert.assertEquals(saEvergreen.getNextPhase().getName(), "shotgun-annual-evergreen");
     }
 }
diff --git a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/overdue/TestOverdueBase.java b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/overdue/TestOverdueBase.java
index ccc25d5..5f5b4e2 100644
--- a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/overdue/TestOverdueBase.java
+++ b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/overdue/TestOverdueBase.java
@@ -27,11 +27,12 @@ import org.killbill.billing.account.api.Account;
 import org.killbill.billing.beatrix.integration.BeatrixIntegrationModule;
 import org.killbill.billing.beatrix.integration.TestIntegrationBase;
 import org.killbill.billing.catalog.api.BillingPeriod;
+import org.killbill.billing.entitlement.api.BlockingState;
 import org.killbill.billing.entitlement.api.BlockingStateType;
 import org.killbill.billing.entitlement.api.SubscriptionBundle;
 import org.killbill.billing.overdue.OverdueService;
-import org.killbill.billing.overdue.api.DefaultOverdueInternalApi;
 import org.killbill.billing.overdue.config.DefaultOverdueConfig;
+import org.killbill.billing.overdue.wrapper.OverdueWrapper;
 import org.killbill.billing.payment.api.PaymentMethodPlugin;
 import org.killbill.billing.payment.api.TestPaymentMethodPluginBase;
 import org.killbill.xmlloader.XMLLoader;
@@ -81,11 +82,15 @@ public abstract class TestOverdueBase extends TestIntegrationBase {
             await().atMost(10, SECONDS).until(new Callable<Boolean>() {
                 @Override
                 public Boolean call() throws Exception {
-                    return expected.equals(blockingApi.getBlockingStateForService(account.getId(), BlockingStateType.ACCOUNT, OverdueService.OVERDUE_SERVICE_NAME, internalCallContext).getStateName());
+                    final BlockingState blockingStateForService = blockingApi.getBlockingStateForService(account.getId(), BlockingStateType.ACCOUNT, OverdueService.OVERDUE_SERVICE_NAME, internalCallContext);
+                    final String stateName = blockingStateForService != null ? blockingStateForService.getStateName() : OverdueWrapper.CLEAR_STATE_NAME;
+                    return expected.equals(stateName);
                 }
             });
         } catch (final Exception e) {
-            Assert.assertEquals(blockingApi.getBlockingStateForService(account.getId(), BlockingStateType.ACCOUNT, OverdueService.OVERDUE_SERVICE_NAME, internalCallContext).getStateName(), expected, "Got exception: " + e.toString());
+            final BlockingState blockingStateForService = blockingApi.getBlockingStateForService(account.getId(), BlockingStateType.ACCOUNT, OverdueService.OVERDUE_SERVICE_NAME, internalCallContext);
+            final String stateName = blockingStateForService != null ? blockingStateForService.getStateName() : OverdueWrapper.CLEAR_STATE_NAME;
+            Assert.assertEquals(stateName, expected, "Got exception: " + e.toString());
         }
     }
 }
diff --git a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/overdue/TestOverdueIntegration.java b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/overdue/TestOverdueIntegration.java
index 5d44b22..d0797dd 100644
--- a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/overdue/TestOverdueIntegration.java
+++ b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/overdue/TestOverdueIntegration.java
@@ -45,7 +45,7 @@ import org.killbill.billing.invoice.api.InvoiceItem;
 import org.killbill.billing.invoice.api.InvoiceItemType;
 import org.killbill.billing.invoice.api.InvoicePayment;
 import org.killbill.billing.invoice.model.ExternalChargeInvoiceItem;
-import org.killbill.billing.junction.DefaultBlockingState;
+import org.killbill.billing.overdue.wrapper.OverdueWrapper;
 import org.killbill.billing.payment.api.Payment;
 import org.killbill.billing.payment.api.PluginProperty;
 import org.testng.Assert;
@@ -137,15 +137,15 @@ public class TestOverdueIntegration extends TestOverdueBase {
 
         // 2012-06-08 => Retry P0
         addDaysAndCheckForCompletion(8, NextEvent.PAYMENT_ERROR, NextEvent.INVOICE_PAYMENT_ERROR);
-        checkODState(DefaultBlockingState.CLEAR_STATE_NAME);
+        checkODState(OverdueWrapper.CLEAR_STATE_NAME);
 
         // 2012-06-16 => Retry P0
         addDaysAndCheckForCompletion(8, NextEvent.PAYMENT_ERROR, NextEvent.INVOICE_PAYMENT_ERROR);
-        checkODState(DefaultBlockingState.CLEAR_STATE_NAME);
+        checkODState(OverdueWrapper.CLEAR_STATE_NAME);
 
         // 2012-06-24 => Retry P0
         addDaysAndCheckForCompletion(8, NextEvent.PAYMENT_ERROR, NextEvent.INVOICE_PAYMENT_ERROR);
-        checkODState(DefaultBlockingState.CLEAR_STATE_NAME);
+        checkODState(OverdueWrapper.CLEAR_STATE_NAME);
 
         // 2012-06-30 => P1
         addDaysAndCheckForCompletion(6, NextEvent.BLOCK, NextEvent.INVOICE, NextEvent.PAYMENT_ERROR, NextEvent.INVOICE_PAYMENT_ERROR);
@@ -224,15 +224,15 @@ public class TestOverdueIntegration extends TestOverdueBase {
 
         // 2012-06-08 => Retry P0
         addDaysAndCheckForCompletion(8, NextEvent.PAYMENT_ERROR, NextEvent.INVOICE_PAYMENT_ERROR);
-        checkODState(DefaultBlockingState.CLEAR_STATE_NAME);
+        checkODState(OverdueWrapper.CLEAR_STATE_NAME);
 
         // 2012-06-16 => Retry P0
         addDaysAndCheckForCompletion(8, NextEvent.PAYMENT_ERROR, NextEvent.INVOICE_PAYMENT_ERROR);
-        checkODState(DefaultBlockingState.CLEAR_STATE_NAME);
+        checkODState(OverdueWrapper.CLEAR_STATE_NAME);
 
         // 2012-06-24 => Retry P0
         addDaysAndCheckForCompletion(8, NextEvent.PAYMENT_ERROR, NextEvent.INVOICE_PAYMENT_ERROR);
-        checkODState(DefaultBlockingState.CLEAR_STATE_NAME);
+        checkODState(OverdueWrapper.CLEAR_STATE_NAME);
 
         // 2012-06-30 => P1
         addDaysAndCheckForCompletion(6, NextEvent.BLOCK, NextEvent.INVOICE, NextEvent.PAYMENT_ERROR, NextEvent.INVOICE_PAYMENT_ERROR);
@@ -318,15 +318,15 @@ public class TestOverdueIntegration extends TestOverdueBase {
 
         // 2012-06-08 => Retry P0
         addDaysAndCheckForCompletion(8, NextEvent.PAYMENT_ERROR, NextEvent.INVOICE_PAYMENT_ERROR);
-        checkODState(DefaultBlockingState.CLEAR_STATE_NAME);
+        checkODState(OverdueWrapper.CLEAR_STATE_NAME);
 
         // 2012-06-16 => Retry P0
         addDaysAndCheckForCompletion(8, NextEvent.PAYMENT_ERROR, NextEvent.INVOICE_PAYMENT_ERROR);
-        checkODState(DefaultBlockingState.CLEAR_STATE_NAME);
+        checkODState(OverdueWrapper.CLEAR_STATE_NAME);
 
         // 2012-06-24 => Retry P0
         addDaysAndCheckForCompletion(8, NextEvent.PAYMENT_ERROR, NextEvent.INVOICE_PAYMENT_ERROR);
-        checkODState(DefaultBlockingState.CLEAR_STATE_NAME);
+        checkODState(OverdueWrapper.CLEAR_STATE_NAME);
 
         // 2012-06-30
         addDaysAndCheckForCompletion(6, NextEvent.BLOCK, NextEvent.INVOICE, NextEvent.PAYMENT_ERROR, NextEvent.INVOICE_PAYMENT_ERROR);
@@ -418,15 +418,15 @@ public class TestOverdueIntegration extends TestOverdueBase {
 
         // 2012-06-08 => Retry P0
         addDaysAndCheckForCompletion(8, NextEvent.PAYMENT_ERROR, NextEvent.INVOICE_PAYMENT_ERROR);
-        checkODState(DefaultBlockingState.CLEAR_STATE_NAME);
+        checkODState(OverdueWrapper.CLEAR_STATE_NAME);
 
         // 2012-06-16 => Retry P0
         addDaysAndCheckForCompletion(8, NextEvent.PAYMENT_ERROR, NextEvent.INVOICE_PAYMENT_ERROR);
-        checkODState(DefaultBlockingState.CLEAR_STATE_NAME);
+        checkODState(OverdueWrapper.CLEAR_STATE_NAME);
 
         // 2012-06-24 => Retry P0
         addDaysAndCheckForCompletion(8, NextEvent.PAYMENT_ERROR, NextEvent.INVOICE_PAYMENT_ERROR);
-        checkODState(DefaultBlockingState.CLEAR_STATE_NAME);
+        checkODState(OverdueWrapper.CLEAR_STATE_NAME);
 
         // 2012-06-30
         addDaysAndCheckForCompletion(6, NextEvent.BLOCK, NextEvent.INVOICE, NextEvent.PAYMENT_ERROR, NextEvent.INVOICE_PAYMENT_ERROR);
@@ -507,15 +507,15 @@ public class TestOverdueIntegration extends TestOverdueBase {
 
         // 2012-06-08 => Retry P0
         addDaysAndCheckForCompletion(8, NextEvent.PAYMENT_ERROR, NextEvent.INVOICE_PAYMENT_ERROR);
-        checkODState(DefaultBlockingState.CLEAR_STATE_NAME);
+        checkODState(OverdueWrapper.CLEAR_STATE_NAME);
 
         // 2012-06-16 => Retry P0
         addDaysAndCheckForCompletion(8, NextEvent.PAYMENT_ERROR, NextEvent.INVOICE_PAYMENT_ERROR);
-        checkODState(DefaultBlockingState.CLEAR_STATE_NAME);
+        checkODState(OverdueWrapper.CLEAR_STATE_NAME);
 
         // 2012-06-24 => Retry P0
         addDaysAndCheckForCompletion(8, NextEvent.PAYMENT_ERROR, NextEvent.INVOICE_PAYMENT_ERROR);
-        checkODState(DefaultBlockingState.CLEAR_STATE_NAME);
+        checkODState(OverdueWrapper.CLEAR_STATE_NAME);
 
         // 2012-06-30 => OD1
         addDaysAndCheckForCompletion(6, NextEvent.BLOCK);
@@ -586,13 +586,13 @@ public class TestOverdueIntegration extends TestOverdueBase {
         invoiceChecker.checkChargedThroughDate(baseEntitlement.getId(), new LocalDate(2012, 6, 30), callContext);
 
         // Should still be in clear state
-        checkODState(DefaultBlockingState.CLEAR_STATE_NAME);
+        checkODState(OverdueWrapper.CLEAR_STATE_NAME);
 
         // 2012-06-15 => DAY 45 - 15 days after invoice
         addDaysAndCheckForCompletion(15);
 
         // Should still be in clear state
-        checkODState(DefaultBlockingState.CLEAR_STATE_NAME);
+        checkODState(OverdueWrapper.CLEAR_STATE_NAME);
 
         // 2012-07-05 => DAY 65 - 35 days after invoice
         // Single PAYMENT_ERROR here here triggered by the invoice
@@ -686,12 +686,12 @@ public class TestOverdueIntegration extends TestOverdueBase {
 
         // Should still be in clear state - the invoice for the bundle has been paid, but not the invoice with the external charge (because it is in draft mode)
         // We refresh overdue just to be safe, see below
-        checkODState(DefaultBlockingState.CLEAR_STATE_NAME);
+        checkODState(OverdueWrapper.CLEAR_STATE_NAME);
 
         // 2012-06-06 => Past 30 days since the external charge
         addDaysAndCheckForCompletion(6);
         // We should still be clear
-        checkODState(DefaultBlockingState.CLEAR_STATE_NAME);
+        checkODState(OverdueWrapper.CLEAR_STATE_NAME);
 
         Assert.assertEquals(invoiceUserApi.getUnpaidInvoicesByAccountId(account.getId(), clock.getUTCToday(), callContext).size(), 0);
     }
@@ -715,13 +715,13 @@ public class TestOverdueIntegration extends TestOverdueBase {
         invoiceChecker.checkChargedThroughDate(baseEntitlement.getId(), new LocalDate(2012, 6, 30), callContext);
 
         // Should still be in clear state
-        checkODState(DefaultBlockingState.CLEAR_STATE_NAME);
+        checkODState(OverdueWrapper.CLEAR_STATE_NAME);
 
         // 2012-06-15 => DAY 45 - 15 days after invoice
         addDaysAndCheckForCompletion(15);
 
         // Should still be in clear state
-        checkODState(DefaultBlockingState.CLEAR_STATE_NAME);
+        checkODState(OverdueWrapper.CLEAR_STATE_NAME);
 
         // 2012-07-05 => DAY 65 - 35 days after invoice
         addDaysAndCheckForCompletion(20, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
@@ -730,7 +730,7 @@ public class TestOverdueIntegration extends TestOverdueBase {
         invoiceChecker.checkChargedThroughDate(baseEntitlement.getId(), new LocalDate(2012, 7, 31), callContext);
 
         // Should still be in clear state
-        checkODState(DefaultBlockingState.CLEAR_STATE_NAME);
+        checkODState(OverdueWrapper.CLEAR_STATE_NAME);
 
         // Now, refund the second (first non-zero dollar) invoice
         final Payment payment = paymentApi.getPayment(invoiceUserApi.getInvoicesByAccount(account.getId(), callContext).get(1).getPayments().get(0).getPaymentId(), false, PLUGIN_PROPERTIES, callContext);
@@ -759,13 +759,13 @@ public class TestOverdueIntegration extends TestOverdueBase {
         invoiceChecker.checkChargedThroughDate(baseEntitlement.getId(), new LocalDate(2012, 6, 30), callContext);
 
         // Should still be in clear state
-        checkODState(DefaultBlockingState.CLEAR_STATE_NAME);
+        checkODState(OverdueWrapper.CLEAR_STATE_NAME);
 
         // 2012-06-15 => DAY 45 - 15 days after invoice
         addDaysAndCheckForCompletion(15);
 
         // Should still be in clear state
-        checkODState(DefaultBlockingState.CLEAR_STATE_NAME);
+        checkODState(OverdueWrapper.CLEAR_STATE_NAME);
 
         // 2012-07-05 => DAY 65 - 35 days after invoice
         addDaysAndCheckForCompletion(20, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
@@ -774,7 +774,7 @@ public class TestOverdueIntegration extends TestOverdueBase {
         invoiceChecker.checkChargedThroughDate(baseEntitlement.getId(), new LocalDate(2012, 7, 31), callContext);
 
         // Should still be in clear state
-        checkODState(DefaultBlockingState.CLEAR_STATE_NAME);
+        checkODState(OverdueWrapper.CLEAR_STATE_NAME);
 
         // Now, create a chargeback for the second (first non-zero dollar) invoice
         final InvoicePayment invoicePayment = invoicePaymentApi.getInvoicePayments(invoiceUserApi.getInvoicesByAccount(account.getId(), callContext).get(1).getPayments().get(0).getPaymentId(), callContext).get(0);
@@ -805,13 +805,13 @@ public class TestOverdueIntegration extends TestOverdueBase {
         invoiceChecker.checkChargedThroughDate(baseEntitlement.getId(), new LocalDate(2012, 6, 30), callContext);
 
         // Should still be in clear state
-        checkODState(DefaultBlockingState.CLEAR_STATE_NAME);
+        checkODState(OverdueWrapper.CLEAR_STATE_NAME);
 
         // 2012-06-15 => DAY 45 - 15 days after invoice
         addDaysAndCheckForCompletion(15, NextEvent.PAYMENT_ERROR, NextEvent.INVOICE_PAYMENT_ERROR);
 
         // Should still be in clear state
-        checkODState(DefaultBlockingState.CLEAR_STATE_NAME);
+        checkODState(OverdueWrapper.CLEAR_STATE_NAME);
 
         // 2012-07-05 => DAY 65 - 35 days after invoice
         addDaysAndCheckForCompletion(20, NextEvent.BLOCK, NextEvent.INVOICE, NextEvent.PAYMENT_ERROR, NextEvent.INVOICE_PAYMENT_ERROR, NextEvent.PAYMENT_ERROR, NextEvent.INVOICE_PAYMENT_ERROR);
@@ -830,7 +830,7 @@ public class TestOverdueIntegration extends TestOverdueBase {
         final Invoice firstNonZeroInvoice = invoiceUserApi.getUnpaidInvoicesByAccountId(account.getId(), clock.getUTCToday(), callContext).iterator().next();
         createExternalPaymentAndCheckForCompletion(account, firstNonZeroInvoice, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT, NextEvent.BLOCK);
         // We should be clear now
-        checkODState(DefaultBlockingState.CLEAR_STATE_NAME);
+        checkODState(OverdueWrapper.CLEAR_STATE_NAME);
     }
 
     @Test(groups = "slow", description = "Test overdue clear after item adjustment")
@@ -853,13 +853,13 @@ public class TestOverdueIntegration extends TestOverdueBase {
         invoiceChecker.checkChargedThroughDate(baseEntitlement.getId(), new LocalDate(2012, 6, 30), callContext);
 
         // Should still be in clear state
-        checkODState(DefaultBlockingState.CLEAR_STATE_NAME);
+        checkODState(OverdueWrapper.CLEAR_STATE_NAME);
 
         // DAY 45 - 15 days after invoice
         addDaysAndCheckForCompletion(15, NextEvent.PAYMENT_ERROR, NextEvent.INVOICE_PAYMENT_ERROR);
 
         // Should still be in clear state
-        checkODState(DefaultBlockingState.CLEAR_STATE_NAME);
+        checkODState(OverdueWrapper.CLEAR_STATE_NAME);
 
         // DAY 65 - 35 days after invoice
         addDaysAndCheckForCompletion(20, NextEvent.BLOCK, NextEvent.INVOICE, NextEvent.PAYMENT_ERROR, NextEvent.INVOICE_PAYMENT_ERROR, NextEvent.PAYMENT_ERROR, NextEvent.INVOICE_PAYMENT_ERROR);
@@ -877,7 +877,7 @@ public class TestOverdueIntegration extends TestOverdueBase {
         final Invoice firstNonZeroInvoice = invoiceUserApi.getUnpaidInvoicesByAccountId(account.getId(), clock.getUTCToday(), callContext).iterator().next();
         fullyAdjustInvoiceItemAndCheckForCompletion(account, firstNonZeroInvoice, 1, NextEvent.BLOCK, NextEvent.INVOICE_ADJUSTMENT);
         // We should be clear now
-        checkODState(DefaultBlockingState.CLEAR_STATE_NAME);
+        checkODState(OverdueWrapper.CLEAR_STATE_NAME);
 
         invoiceChecker.checkInvoice(account.getId(), 2,
                                     callContext, new ExpectedInvoiceItemCheck(new LocalDate(2012, 5, 31), new LocalDate(2012, 6, 30), InvoiceItemType.RECURRING, new BigDecimal("249.95")),
@@ -888,13 +888,13 @@ public class TestOverdueIntegration extends TestOverdueBase {
         addDaysAndCheckForCompletion(5);
 
         // We should still be clear
-        checkODState(DefaultBlockingState.CLEAR_STATE_NAME);
+        checkODState(OverdueWrapper.CLEAR_STATE_NAME);
 
         // DAY 80 - 20 days after second invoice
         addDaysAndCheckForCompletion(10, NextEvent.PAYMENT_ERROR, NextEvent.INVOICE_PAYMENT_ERROR);
 
         // We should still be clear
-        checkODState(DefaultBlockingState.CLEAR_STATE_NAME);
+        checkODState(OverdueWrapper.CLEAR_STATE_NAME);
 
         // DAY 95 - 35 days after second invoice
         addDaysAndCheckForCompletion(15, NextEvent.BLOCK, NextEvent.PAYMENT_ERROR, NextEvent.INVOICE_PAYMENT_ERROR, NextEvent.INVOICE, NextEvent.PAYMENT_ERROR, NextEvent.INVOICE_PAYMENT_ERROR);
@@ -916,12 +916,7 @@ public class TestOverdueIntegration extends TestOverdueBase {
         }
 
         // We should be cleared again
-        checkODState(DefaultBlockingState.CLEAR_STATE_NAME);
-    }
-
-    @Test(groups = "slow", enabled = false)
-    public void testOverdueStateAndWRITTEN_OFFTag() throws Exception {
-        // TODO add/remove tag to invoice
+        checkODState(OverdueWrapper.CLEAR_STATE_NAME);
     }
 
     private void allowPaymentsAndResetOverdueToClearByPayingAllUnpaidInvoices(final boolean extraPayment) {
@@ -951,7 +946,7 @@ public class TestOverdueIntegration extends TestOverdueBase {
                 }
             }
         }
-        checkODState(DefaultBlockingState.CLEAR_STATE_NAME);
+        checkODState(OverdueWrapper.CLEAR_STATE_NAME);
     }
 
     private List<Invoice> getUnpaidInvoicesOrderFromRecent() {
diff --git a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/overdue/TestOverdueWithSubscriptionCancellation.java b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/overdue/TestOverdueWithSubscriptionCancellation.java
index 026d500..9575752 100644
--- a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/overdue/TestOverdueWithSubscriptionCancellation.java
+++ b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/overdue/TestOverdueWithSubscriptionCancellation.java
@@ -31,6 +31,7 @@ import org.killbill.billing.entitlement.api.Entitlement.EntitlementState;
 import org.killbill.billing.entitlement.api.SubscriptionBundle;
 import org.killbill.billing.invoice.api.InvoiceItemType;
 import org.killbill.billing.junction.DefaultBlockingState;
+import org.killbill.billing.overdue.wrapper.OverdueWrapper;
 import org.killbill.billing.subscription.api.SubscriptionBase;
 import org.testng.annotations.Test;
 
@@ -92,7 +93,7 @@ public class TestOverdueWithSubscriptionCancellation extends TestOverdueBase {
 
 
         // Should still be in clear state
-        checkODState(DefaultBlockingState.CLEAR_STATE_NAME);
+        checkODState(OverdueWrapper.CLEAR_STATE_NAME);
 
 
         // DAY 36 -- RIGHT AFTER OD1 (two block events, for the cancellation and the OD1 state)
@@ -148,7 +149,7 @@ public class TestOverdueWithSubscriptionCancellation extends TestOverdueBase {
         invoiceChecker.checkChargedThroughDate(baseEntitlement.getId(), new LocalDate(2012, 6, 30), callContext);
 
         // Should still be in clear state
-        checkODState(DefaultBlockingState.CLEAR_STATE_NAME);
+        checkODState(OverdueWrapper.CLEAR_STATE_NAME);
 
         // DAY 36 (2012-06-06)-- RIGHT AFTER OD1 (two block events, for the cancellation and the OD1 state)
         // One BLOCK event is for the overdue state transition
diff --git a/beatrix/src/test/java/org/killbill/billing/beatrix/util/InvoiceChecker.java b/beatrix/src/test/java/org/killbill/billing/beatrix/util/InvoiceChecker.java
index 77bc381..7a10f54 100644
--- a/beatrix/src/test/java/org/killbill/billing/beatrix/util/InvoiceChecker.java
+++ b/beatrix/src/test/java/org/killbill/billing/beatrix/util/InvoiceChecker.java
@@ -1,7 +1,9 @@
 /*
  * Copyright 2010-2013 Ning, Inc.
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 The Billing Project, LLC
  *
- * Ning licenses this file to you under the Apache License, version 2.0
+ * The Billing Project licenses this file to you under the Apache License, version 2.0
  * (the "License"); you may not use this file except in compliance with the
  * License.  You may obtain a copy of the License at:
  *
@@ -159,17 +161,11 @@ public class InvoiceChecker {
             if (expectedLocalCTD == null) {
                 assertNull(subscription.getChargedThroughDate());
             } else {
-                assertTrue(expectedLocalCTD.compareTo(subscription.getChargedThroughDate().toLocalDate()) == 0);
-                /*
-                final DateTime expectedCTD = expectedLocalCTD.toDateTime(new LocalTime(subscription.getStartDate().getMillis(), DateTimeZone.UTC), DateTimeZone.UTC);
-                final String msg = String.format("Checking CTD for entitlement %s : expectedLocalCTD = %s => expectedCTD = %s, got %s",
-                                                 entitlementId, expectedLocalCTD, expectedCTD, subscription.getChargedThroughDate());
-                log.info(msg);
-                assertNotNull(subscription.getChargedThroughDate());
-                assertTrue(subscription.getChargedThroughDate().compareTo(expectedCTD) == 0, msg);
-                */
+                final String msg = String.format("Checking CTD for entitlement %s : expectedLocalCTD = %s, got %s",
+                                                 entitlementId, expectedLocalCTD, subscription.getChargedThroughDate().toLocalDate());
+                assertTrue(expectedLocalCTD.compareTo(subscription.getChargedThroughDate().toLocalDate()) == 0, msg);
             }
-        } catch (EntitlementApiException e) {
+        } catch (final EntitlementApiException e) {
             fail("Failed to retrieve entitlement for " + entitlementId);
         }
     }
@@ -182,7 +178,7 @@ public class InvoiceChecker {
         private final InvoiceItemType type;
         private final BigDecimal amount;
 
-        public ExpectedInvoiceItemCheck(final InvoiceItemType type, final BigDecimal amount, boolean checkDates) {
+        public ExpectedInvoiceItemCheck(final InvoiceItemType type, final BigDecimal amount, final boolean checkDates) {
             this.checkDates = checkDates;
             this.type = type;
             this.startDate = null;
@@ -195,7 +191,7 @@ public class InvoiceChecker {
         }
 
         public ExpectedInvoiceItemCheck(final LocalDate startDate, final LocalDate endDate,
-                                        final InvoiceItemType type, final BigDecimal amount, boolean checkDates) {
+                                        final InvoiceItemType type, final BigDecimal amount, final boolean checkDates) {
             this.checkDates = checkDates;
             this.startDate = startDate;
             this.endDate = endDate;
@@ -227,6 +223,17 @@ public class InvoiceChecker {
         public BigDecimal getAmount() {
             return amount;
         }
-    }
 
+        @Override
+        public String toString() {
+            final StringBuilder sb = new StringBuilder("ExpectedInvoiceItemCheck{");
+            sb.append("checkDates=").append(checkDates);
+            sb.append(", startDate=").append(startDate);
+            sb.append(", endDate=").append(endDate);
+            sb.append(", type=").append(type);
+            sb.append(", amount=").append(amount);
+            sb.append('}');
+            return sb.toString();
+        }
+    }
 }
diff --git a/entitlement/src/main/java/org/killbill/billing/entitlement/api/DefaultEntitlement.java b/entitlement/src/main/java/org/killbill/billing/entitlement/api/DefaultEntitlement.java
index a4d2148..93462b9 100644
--- a/entitlement/src/main/java/org/killbill/billing/entitlement/api/DefaultEntitlement.java
+++ b/entitlement/src/main/java/org/killbill/billing/entitlement/api/DefaultEntitlement.java
@@ -288,44 +288,55 @@ public class DefaultEntitlement extends EntityBase implements Entitlement {
         // Get the latest state from disk
         refresh(callContext);
 
-        if (eventsStream.isEntitlementCancelled()) {
-            throw new EntitlementApiException(ErrorCode.SUB_CANCEL_BAD_STATE, getId(), EntitlementState.CANCELLED);
-        }
+        final EntitlementContext pluginContext = new DefaultEntitlementContext(OperationType.CANCEL_SUBSCRIPTION,
+                                                                               getAccountId(),
+                                                                               null,
+                                                                               getBundleId(),
+                                                                               getExternalKey(),
+                                                                               null,
+                                                                               localCancelDate,
+                                                                               properties,
+                                                                               callContext);
 
-        final InternalCallContext contextWithValidAccountRecordId = internalCallContextFactory.createInternalCallContext(getAccountId(), callContext);
-        final DateTime effectiveCancelDate = dateHelper.fromLocalDateAndReferenceTime(localCancelDate, getSubscriptionBase().getStartDate(), contextWithValidAccountRecordId);
-        try {
-            if (overrideBillingEffectiveDate) {
-                getSubscriptionBase().cancelWithDate(effectiveCancelDate, callContext);
-            } else {
-                getSubscriptionBase().cancel(callContext);
-            }
-        } catch (final SubscriptionBaseApiException e) {
-            throw new EntitlementApiException(e);
-        }
 
-        final BlockingState newBlockingState = new DefaultBlockingState(getId(), BlockingStateType.SUBSCRIPTION, DefaultEntitlementApi.ENT_STATE_CANCELLED, EntitlementService.ENTITLEMENT_SERVICE_NAME, true, true, false, effectiveCancelDate);
-        final Collection<NotificationEvent> notificationEvents = new ArrayList<NotificationEvent>();
-        final Collection<BlockingState> addOnsBlockingStates = computeAddOnBlockingStates(effectiveCancelDate, notificationEvents, callContext, contextWithValidAccountRecordId);
+        final WithEntitlementPlugin<Entitlement> cancelEntitlementWithPlugin = new WithEntitlementPlugin<Entitlement>() {
 
-        // Record the new state first, then insert the notifications to avoid race conditions
-        setBlockingStates(newBlockingState, addOnsBlockingStates, contextWithValidAccountRecordId);
-        for (final NotificationEvent notificationEvent : notificationEvents) {
-            recordFutureNotification(effectiveCancelDate, notificationEvent, contextWithValidAccountRecordId);
-        }
+            @Override
+            public Entitlement doCall(final EntitlementApi entitlementApi, final EntitlementContext updatedPluginContext) throws EntitlementApiException {
+                if (eventsStream.isEntitlementCancelled()) {
+                    throw new EntitlementApiException(ErrorCode.SUB_CANCEL_BAD_STATE, getId(), EntitlementState.CANCELLED);
+                }
 
-        return entitlementApi.getEntitlementForId(getId(), callContext);
-    }
+                final InternalCallContext contextWithValidAccountRecordId = internalCallContextFactory.createInternalCallContext(getAccountId(), callContext);
+                final DateTime effectiveCancelDate = dateHelper.fromLocalDateAndReferenceTime(localCancelDate, getSubscriptionBase().getStartDate(), contextWithValidAccountRecordId);
+                try {
+                    if (overrideBillingEffectiveDate) {
+                        getSubscriptionBase().cancelWithDate(effectiveCancelDate, callContext);
+                    } else {
+                        getSubscriptionBase().cancel(callContext);
+                    }
+                } catch (final SubscriptionBaseApiException e) {
+                    throw new EntitlementApiException(e);
+                }
 
-    @Override
-    public Entitlement cancelEntitlementWithPolicyOverrideBillingPolicy(final EntitlementActionPolicy entitlementPolicy, final BillingActionPolicy billingPolicy, final Iterable<PluginProperty> properties, final CallContext callContext) throws EntitlementApiException {
-        // Get the latest state from disk - required to have the latest CTD
-        refresh(callContext);
+                final BlockingState newBlockingState = new DefaultBlockingState(getId(), BlockingStateType.SUBSCRIPTION, DefaultEntitlementApi.ENT_STATE_CANCELLED, EntitlementService.ENTITLEMENT_SERVICE_NAME, true, true, false, effectiveCancelDate);
+                final Collection<NotificationEvent> notificationEvents = new ArrayList<NotificationEvent>();
+                final Collection<BlockingState> addOnsBlockingStates = computeAddOnBlockingStates(effectiveCancelDate, notificationEvents, callContext, contextWithValidAccountRecordId);
 
-        final LocalDate cancellationDate = getLocalDateFromEntitlementPolicy(entitlementPolicy);
-        return cancelEntitlementWithDateOverrideBillingPolicy(cancellationDate, billingPolicy, properties, callContext);
+                // Record the new state first, then insert the notifications to avoid race conditions
+                setBlockingStates(newBlockingState, addOnsBlockingStates, contextWithValidAccountRecordId);
+                for (final NotificationEvent notificationEvent : notificationEvents) {
+                    recordFutureNotification(effectiveCancelDate, notificationEvent, contextWithValidAccountRecordId);
+                }
+
+                return entitlementApi.getEntitlementForId(getId(), callContext);
+            }
+        };
+
+        return pluginExecution.executeWithPlugin(cancelEntitlementWithPlugin, pluginContext);
     }
 
+
     @Override
     public void uncancelEntitlement(final Iterable<PluginProperty> properties, final CallContext callContext) throws EntitlementApiException {
 
@@ -368,6 +379,16 @@ public class DefaultEntitlement extends EntityBase implements Entitlement {
         }
     }
 
+    @Override
+    public Entitlement cancelEntitlementWithPolicyOverrideBillingPolicy(final EntitlementActionPolicy entitlementPolicy, final BillingActionPolicy billingPolicy, final Iterable<PluginProperty> properties, final CallContext callContext) throws EntitlementApiException {
+        // Get the latest state from disk - required to have the latest CTD
+        refresh(callContext);
+
+        final LocalDate cancellationDate = getLocalDateFromEntitlementPolicy(entitlementPolicy);
+        return cancelEntitlementWithDateOverrideBillingPolicy(cancellationDate, billingPolicy, properties, callContext);
+    }
+
+
     // See also EntitlementInternalApi#cancel for the bulk API
     @Override
     public Entitlement cancelEntitlementWithDateOverrideBillingPolicy(final LocalDate localCancelDate, final BillingActionPolicy billingPolicy, final Iterable<PluginProperty> properties, final CallContext callContext) throws EntitlementApiException {
diff --git a/entitlement/src/main/java/org/killbill/billing/entitlement/api/svcs/DefaultEntitlementApiBase.java b/entitlement/src/main/java/org/killbill/billing/entitlement/api/svcs/DefaultEntitlementApiBase.java
index 0173d7c..59f2c89 100644
--- a/entitlement/src/main/java/org/killbill/billing/entitlement/api/svcs/DefaultEntitlementApiBase.java
+++ b/entitlement/src/main/java/org/killbill/billing/entitlement/api/svcs/DefaultEntitlementApiBase.java
@@ -168,11 +168,6 @@ public class DefaultEntitlementApiBase {
             public Void doCall(final EntitlementApi entitlementApi, final EntitlementContext updatedPluginContext) throws EntitlementApiException {
                 try {
 
-                    final BlockingState currentState = blockingStateDao.getBlockingStateForService(bundleId, BlockingStateType.SUBSCRIPTION_BUNDLE, EntitlementService.ENTITLEMENT_SERVICE_NAME, internalCallContext);
-                    if (currentState != null && currentState.getStateName().equals(DefaultEntitlementApi.ENT_STATE_BLOCKED)) {
-                        throw new EntitlementApiException(ErrorCode.ENT_ALREADY_BLOCKED, bundleId);
-                    }
-
                     final SubscriptionBaseBundle bundle = subscriptionInternalApi.getBundleFromId(bundleId, internalCallContext);
                     final ImmutableAccountData account = accountApi.getImmutableAccountDataById(bundle.getAccountId(), internalCallContext);
                     final SubscriptionBase baseSubscription = subscriptionInternalApi.getBaseSubscription(bundleId, internalCallContext);
@@ -235,13 +230,6 @@ public class DefaultEntitlementApiBase {
                         return null;
                     }
 
-                    final BlockingState currentState = blockingStateDao.getBlockingStateForService(bundleId, BlockingStateType.SUBSCRIPTION_BUNDLE, EntitlementService.ENTITLEMENT_SERVICE_NAME, internalCallContext);
-                    if (currentState == null || currentState.getStateName().equals(DefaultEntitlementApi.ENT_STATE_CLEAR)) {
-                        // Nothing to do.
-                        log.warn("Current state is {}, nothing to resume", currentState);
-                        return null;
-                    }
-
                     final UUID blockingId = blockUnblockBundle(bundleId, DefaultEntitlementApi.ENT_STATE_CLEAR, EntitlementService.ENTITLEMENT_SERVICE_NAME, localEffectiveDate, false, false, false, baseSubscription, internalCallContext);
 
                     // Should we send one event per entitlement in the bundle?
diff --git a/entitlement/src/main/java/org/killbill/billing/entitlement/api/svcs/DefaultInternalBlockingApi.java b/entitlement/src/main/java/org/killbill/billing/entitlement/api/svcs/DefaultInternalBlockingApi.java
index 971786e..007c423 100644
--- a/entitlement/src/main/java/org/killbill/billing/entitlement/api/svcs/DefaultInternalBlockingApi.java
+++ b/entitlement/src/main/java/org/killbill/billing/entitlement/api/svcs/DefaultInternalBlockingApi.java
@@ -21,6 +21,7 @@ import java.util.UUID;
 
 import org.killbill.billing.callcontext.InternalCallContext;
 import org.killbill.billing.callcontext.InternalTenantContext;
+import org.killbill.billing.overdue.OverdueService;
 import org.killbill.clock.Clock;
 import org.killbill.billing.entitlement.api.BlockingState;
 import org.killbill.billing.entitlement.api.BlockingStateType;
@@ -46,12 +47,7 @@ public class DefaultInternalBlockingApi implements BlockingInternalApi {
 
     @Override
     public BlockingState getBlockingStateForService(final UUID overdueableId, final BlockingStateType blockingStateType, final String serviceName, final InternalTenantContext context) {
-        final BlockingState blockingStateForService = dao.getBlockingStateForService(overdueableId, blockingStateType, serviceName, context);
-        if (blockingStateForService == null) {
-            return DefaultBlockingState.getClearState(blockingStateType, serviceName, clock);
-        } else {
-            return blockingStateForService;
-        }
+        return dao.getBlockingStateForService(overdueableId, blockingStateType, serviceName, context);
     }
 
     @Override
diff --git a/entitlement/src/test/java/org/killbill/billing/entitlement/api/TestDefaultEntitlementApi.java b/entitlement/src/test/java/org/killbill/billing/entitlement/api/TestDefaultEntitlementApi.java
index 9a397ae..74fda0c 100644
--- a/entitlement/src/test/java/org/killbill/billing/entitlement/api/TestDefaultEntitlementApi.java
+++ b/entitlement/src/test/java/org/killbill/billing/entitlement/api/TestDefaultEntitlementApi.java
@@ -311,14 +311,6 @@ public class TestDefaultEntitlementApi extends EntitlementTestSuiteWithEmbeddedD
         final Entitlement telescopicEntitlement2 = entitlementApi.getEntitlementForId(telescopicEntitlement.getId(), callContext);
         assertEquals(telescopicEntitlement2.getState(), EntitlementState.BLOCKED);
 
-        // Check we can't block in a blocked state
-        try {
-            entitlementApi.pause(baseEntitlement.getBundleId(), new LocalDate(clock.getUTCNow()), ImmutableList.<PluginProperty>of(), callContext);
-            Assert.fail("Should not have succeeded to block in a blocked state");
-        } catch (EntitlementApiException e) {
-            assertEquals(e.getCode(), ErrorCode.ENT_ALREADY_BLOCKED.getCode());
-        }
-
         final List<Entitlement> bundleEntitlements2 = entitlementApi.getAllEntitlementsForBundle(telescopicEntitlement2.getBundleId(), callContext);
         assertEquals(bundleEntitlements2.size(), 2);
         for (final Entitlement cur : bundleEntitlements2) {
@@ -339,8 +331,11 @@ public class TestDefaultEntitlementApi extends EntitlementTestSuiteWithEmbeddedD
         entitlementApi.resume(baseEntitlement.getBundleId(), new LocalDate(clock.getUTCNow()), ImmutableList.<PluginProperty>of(), callContext);
         assertListenerStatus();
 
-        // Verify call is idempotent
+        // Verify call is idempotent : The current semantics is to post the RESUME because we went through the operation, but not the BLOCK because the DAO logic
+        // filtered the event as the subscription was already resumed.
+        testListener.pushExpectedEvents(NextEvent.RESUME);
         entitlementApi.resume(baseEntitlement.getBundleId(), new LocalDate(clock.getUTCNow()), ImmutableList.<PluginProperty>of(), callContext);
+        assertListenerStatus();
 
         // Verify blocking state
         final Entitlement baseEntitlement3 = entitlementApi.getEntitlementForId(baseEntitlement.getId(), callContext);
diff --git a/entitlement/src/test/java/org/killbill/billing/entitlement/dao/TestProxyBlockingStateDao.java b/entitlement/src/test/java/org/killbill/billing/entitlement/dao/TestProxyBlockingStateDao.java
index 549f02b..8c93f3a 100644
--- a/entitlement/src/test/java/org/killbill/billing/entitlement/dao/TestProxyBlockingStateDao.java
+++ b/entitlement/src/test/java/org/killbill/billing/entitlement/dao/TestProxyBlockingStateDao.java
@@ -78,7 +78,7 @@ public class TestProxyBlockingStateDao extends EntitlementTestSuiteNoDB {
         final BlockingState bs4 = new DefaultBlockingState(UUID.randomUUID(),
                                                            blockedId,
                                                            blockingStateType,
-                                                           DefaultBlockingState.CLEAR_STATE_NAME,
+                                                           "OD4",
                                                            service,
                                                            false,
                                                            false,
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/api/user/DefaultInvoiceUserApi.java b/invoice/src/main/java/org/killbill/billing/invoice/api/user/DefaultInvoiceUserApi.java
index 1697e4e..92f6b7e 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/api/user/DefaultInvoiceUserApi.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/api/user/DefaultInvoiceUserApi.java
@@ -257,7 +257,7 @@ public class DefaultInvoiceUserApi implements InvoiceUserApi {
         }
 
         return new ExternalChargeInvoiceItem(externalChargeItem.getId(), externalChargeItem.getInvoiceId(), externalChargeItem.getAccountId(),
-                                             externalChargeItem.getPlanName(), externalChargeItem.getStartDate(),
+                                             externalChargeItem.getDescription(), externalChargeItem.getStartDate(),
                                              externalChargeItem.getAmount(), externalChargeItem.getCurrency());
     }
 
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/dao/InvoiceDaoHelper.java b/invoice/src/main/java/org/killbill/billing/invoice/dao/InvoiceDaoHelper.java
index 6f11a8b..b4254f6 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/dao/InvoiceDaoHelper.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/dao/InvoiceDaoHelper.java
@@ -179,7 +179,7 @@ public class InvoiceDaoHelper {
             public boolean apply(final InvoiceModelDao in) {
                 final BigDecimal balance = InvoiceModelDaoHelper.getBalance(in);
                 log.debug("Computed balance={} for invoice={}", balance, in);
-                return (InvoiceStatus.COMMITTED.equals(in.getStatus())) && (balance.compareTo(BigDecimal.ZERO) >= 1) && (upToDate == null || !in.getTargetDate().isAfter(upToDate));
+                return InvoiceStatus.COMMITTED.equals(in.getStatus()) && !in.isWrittenOff() && (balance.compareTo(BigDecimal.ZERO) >= 1) && (upToDate == null || !in.getTargetDate().isAfter(upToDate));
             }
         });
         return new ArrayList<InvoiceModelDao>(unpaidInvoices);
diff --git a/invoice/src/test/java/org/killbill/billing/invoice/api/user/TestDefaultInvoiceUserApi.java b/invoice/src/test/java/org/killbill/billing/invoice/api/user/TestDefaultInvoiceUserApi.java
index ebd73d3..6461dd0 100644
--- a/invoice/src/test/java/org/killbill/billing/invoice/api/user/TestDefaultInvoiceUserApi.java
+++ b/invoice/src/test/java/org/killbill/billing/invoice/api/user/TestDefaultInvoiceUserApi.java
@@ -71,9 +71,11 @@ public class TestDefaultInvoiceUserApi extends InvoiceTestSuiteWithEmbeddedDB {
 
         // Post an external charge
         final BigDecimal externalChargeAmount = BigDecimal.TEN;
-        final InvoiceItem externalCharge = new ExternalChargeInvoiceItem(null, accountId, null, UUID.randomUUID().toString(), clock.getUTCToday(), externalChargeAmount, accountCurrency);
+        final InvoiceItem externalCharge = new ExternalChargeInvoiceItem(null, accountId, null, "description", clock.getUTCToday(), externalChargeAmount, accountCurrency);
         final InvoiceItem externalChargeInvoiceItem = invoiceUserApi.insertExternalCharges(accountId, clock.getUTCToday(), ImmutableList.<InvoiceItem>of(externalCharge), true, callContext).get(0);
         verifyExternalChargeOnNewInvoice(accountBalance, null, externalChargeAmount, externalChargeInvoiceItem);
+
+        assertEquals(externalChargeInvoiceItem.getDescription(), "description");
     }
 
     @Test(groups = "slow")
@@ -125,7 +127,7 @@ public class TestDefaultInvoiceUserApi extends InvoiceTestSuiteWithEmbeddedDB {
         verifyExternalChargeOnExistingInvoice(invoiceBalance, null, externalChargeAmount, externalChargeInvoiceItem);
     }
 
-    @Test(groups = "slow", enabled = false)
+    @Test(groups = "slow")
     public void testOriginalAmountCharged() throws Exception {
 
         final Invoice initialInvoice = invoiceUserApi.getInvoice(invoiceId, callContext);
diff --git a/invoice/src/test/java/org/killbill/billing/invoice/proRations/inAdvance/annual/TestProRation.java b/invoice/src/test/java/org/killbill/billing/invoice/proRations/inAdvance/annual/TestProRation.java
index e56b43f..f1f0399 100644
--- a/invoice/src/test/java/org/killbill/billing/invoice/proRations/inAdvance/annual/TestProRation.java
+++ b/invoice/src/test/java/org/killbill/billing/invoice/proRations/inAdvance/annual/TestProRation.java
@@ -1,7 +1,9 @@
 /*
  * Copyright 2010-2013 Ning, Inc.
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 The Billing Project, LLC
  *
- * Ning licenses this file to you under the Apache License, version 2.0
+ * The Billing Project licenses this file to you under the Apache License, version 2.0
  * (the "License"); you may not use this file except in compliance with the
  * License.  You may obtain a copy of the License at:
  *
@@ -16,17 +18,22 @@
 
 package org.killbill.billing.invoice.proRations.inAdvance.annual;
 
-import static org.killbill.billing.invoice.TestInvoiceHelper.*;
-
 import java.math.BigDecimal;
 
 import org.joda.time.LocalDate;
-import org.testng.annotations.Test;
-
 import org.killbill.billing.catalog.api.BillingPeriod;
 import org.killbill.billing.invoice.model.InvalidDateSequenceException;
 import org.killbill.billing.invoice.proRations.inAdvance.ProRationInAdvanceTestBase;
 import org.killbill.billing.util.currency.KillBillMoney;
+import org.testng.annotations.Test;
+
+import static org.killbill.billing.invoice.TestInvoiceHelper.FORTY_SEVEN;
+import static org.killbill.billing.invoice.TestInvoiceHelper.ONE;
+import static org.killbill.billing.invoice.TestInvoiceHelper.SEVEN;
+import static org.killbill.billing.invoice.TestInvoiceHelper.THREE_HUNDRED_AND_FIFTY_FOUR;
+import static org.killbill.billing.invoice.TestInvoiceHelper.THREE_HUNDRED_AND_FOURTY_NINE;
+import static org.killbill.billing.invoice.TestInvoiceHelper.THREE_HUNDRED_AND_SIXTY_FIVE;
+import static org.killbill.billing.invoice.TestInvoiceHelper.THREE_HUNDRED_AND_SIXTY_SIX;
 
 public class TestProRation extends ProRationInAdvanceTestBase {
 
@@ -55,14 +62,17 @@ public class TestProRation extends ProRationInAdvanceTestBase {
         testCalculateNumberOfBillingCycles(startDate, targetDate, 4, expectedValue);
     }
 
-    // TODO Test fails, needs to be investigated
-    @Test(groups = "fast", enabled = false)
+    @Test(groups = "fast")
     public void testSinglePlanDoubleProRation() throws InvalidDateSequenceException {
         final LocalDate startDate = invoiceUtil.buildDate(2011, 1, 10);
         final LocalDate endDate = invoiceUtil.buildDate(2012, 3, 4);
         final LocalDate targetDate = invoiceUtil.buildDate(2012, 4, 5);
 
-        final BigDecimal expectedValue = BigDecimal.ZERO;
+        // SEVEN is number of days between startDate and expected first billing cycle date (2011, 1, 17);
+        // FORTY_SEVEN is number of days between the second billing cycle date (2012, 1, 17) and the end date (2012, 3, 4);
+        // 2011 has 365 days but 2012 has 366 days
+        final BigDecimal expectedValue = ONE.add(SEVEN.divide(THREE_HUNDRED_AND_SIXTY_FIVE, KillBillMoney.ROUNDING_METHOD))
+                                            .add(FORTY_SEVEN.divide(THREE_HUNDRED_AND_SIXTY_SIX, KillBillMoney.ROUNDING_METHOD));
         testCalculateNumberOfBillingCycles(startDate, endDate, targetDate, 17, expectedValue);
     }
 }
diff --git a/invoice/src/test/java/org/killbill/billing/invoice/TestInvoiceHelper.java b/invoice/src/test/java/org/killbill/billing/invoice/TestInvoiceHelper.java
index 4e2d5f6..3cc1c21 100644
--- a/invoice/src/test/java/org/killbill/billing/invoice/TestInvoiceHelper.java
+++ b/invoice/src/test/java/org/killbill/billing/invoice/TestInvoiceHelper.java
@@ -128,6 +128,7 @@ public class TestInvoiceHelper {
     public static final BigDecimal THIRTY_THREE = new BigDecimal("33.0").setScale(KillBillMoney.MAX_SCALE);
 
     public static final BigDecimal FORTY = new BigDecimal("40.0").setScale(KillBillMoney.MAX_SCALE);
+    public static final BigDecimal FORTY_SEVEN = new BigDecimal("47.0").setScale(KillBillMoney.MAX_SCALE);
     public static final BigDecimal SIXTY_SIX = new BigDecimal("66.0").setScale(KillBillMoney.MAX_SCALE);
     public static final BigDecimal SEVENTY_FIVE = new BigDecimal("75.0").setScale(KillBillMoney.MAX_SCALE);
 
diff --git a/junction/src/test/java/org/killbill/billing/junction/TestDefaultBlockingState.java b/junction/src/test/java/org/killbill/billing/junction/TestDefaultBlockingState.java
index 2bc9e33..08616d2 100644
--- a/junction/src/test/java/org/killbill/billing/junction/TestDefaultBlockingState.java
+++ b/junction/src/test/java/org/killbill/billing/junction/TestDefaultBlockingState.java
@@ -45,7 +45,7 @@ public class TestDefaultBlockingState extends JunctionTestSuiteNoDB {
         final BlockingState bs2 = new DefaultBlockingState(UUID.randomUUID(),
                                                            UUID.randomUUID(),
                                                            BlockingStateType.ACCOUNT,
-                                                           DefaultBlockingState.CLEAR_STATE_NAME,
+                                                           "OD2",
                                                            "test",
                                                            false,
                                                            false,
diff --git a/overdue/src/main/java/org/killbill/billing/overdue/api/DefaultOverdueInternalApi.java b/overdue/src/main/java/org/killbill/billing/overdue/api/DefaultOverdueInternalApi.java
index 81c4002..a2701e4 100644
--- a/overdue/src/main/java/org/killbill/billing/overdue/api/DefaultOverdueInternalApi.java
+++ b/overdue/src/main/java/org/killbill/billing/overdue/api/DefaultOverdueInternalApi.java
@@ -26,6 +26,7 @@ import org.killbill.billing.ErrorCode;
 import org.killbill.billing.account.api.ImmutableAccountData;
 import org.killbill.billing.callcontext.InternalCallContext;
 import org.killbill.billing.callcontext.InternalTenantContext;
+import org.killbill.billing.entitlement.api.BlockingState;
 import org.killbill.billing.entitlement.api.BlockingStateType;
 import org.killbill.billing.junction.BlockingInternalApi;
 import org.killbill.billing.overdue.OverdueInternalApi;
@@ -50,6 +51,7 @@ import org.killbill.clock.Clock;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import com.google.common.base.MoreObjects;
 import com.google.inject.Inject;
 
 public class DefaultOverdueInternalApi implements OverdueInternalApi {
@@ -83,7 +85,8 @@ public class DefaultOverdueInternalApi implements OverdueInternalApi {
     public OverdueState getOverdueStateFor(final ImmutableAccountData overdueable, final TenantContext context) throws OverdueException {
         try {
             final InternalTenantContext internalTenantContext = internalCallContextFactory.createInternalTenantContext(context);
-            final String stateName = accessApi.getBlockingStateForService(overdueable.getId(), BlockingStateType.ACCOUNT, OverdueService.OVERDUE_SERVICE_NAME, internalCallContextFactory.createInternalTenantContext(context)).getStateName();
+            final BlockingState blockingStateForService = accessApi.getBlockingStateForService(overdueable.getId(), BlockingStateType.ACCOUNT, OverdueService.OVERDUE_SERVICE_NAME, internalCallContextFactory.createInternalTenantContext(context));
+            final String stateName = blockingStateForService != null ? blockingStateForService.getStateName() : OverdueWrapper.CLEAR_STATE_NAME;
             final OverdueConfig overdueConfig = overdueConfigCache.getOverdueConfig(internalTenantContext);
             final OverdueStateSet states = ((DefaultOverdueConfig) overdueConfig).getOverdueStatesAccount();
             return states.findState(stateName);
diff --git a/overdue/src/main/java/org/killbill/billing/overdue/config/DefaultOverdueStateSet.java b/overdue/src/main/java/org/killbill/billing/overdue/config/DefaultOverdueStateSet.java
index 0628bae..eb71506 100644
--- a/overdue/src/main/java/org/killbill/billing/overdue/config/DefaultOverdueStateSet.java
+++ b/overdue/src/main/java/org/killbill/billing/overdue/config/DefaultOverdueStateSet.java
@@ -21,27 +21,26 @@ import javax.xml.bind.annotation.XmlAccessorType;
 
 import org.joda.time.LocalDate;
 import org.joda.time.Period;
-
 import org.killbill.billing.ErrorCode;
 import org.killbill.billing.overdue.api.OverdueApiException;
 import org.killbill.billing.overdue.api.OverdueState;
 import org.killbill.billing.overdue.config.api.BillingState;
 import org.killbill.billing.overdue.config.api.OverdueStateSet;
+import org.killbill.billing.overdue.wrapper.OverdueWrapper;
 import org.killbill.xmlloader.ValidatingConfig;
 import org.killbill.xmlloader.ValidationErrors;
-import org.killbill.billing.junction.DefaultBlockingState;
 
 @XmlAccessorType(XmlAccessType.NONE)
 public abstract class DefaultOverdueStateSet extends ValidatingConfig<DefaultOverdueConfig> implements OverdueStateSet {
 
     private static final Period ZERO_PERIOD = new Period();
-    private final DefaultOverdueState clearState = new DefaultOverdueState().setName(DefaultBlockingState.CLEAR_STATE_NAME).setClearState(true);
+    private final DefaultOverdueState clearState = new DefaultOverdueState().setName(OverdueWrapper.CLEAR_STATE_NAME).setClearState(true);
 
     public abstract DefaultOverdueState[] getStates();
 
     @Override
     public OverdueState findState(final String stateName) throws OverdueApiException {
-        if (stateName.equals(DefaultBlockingState.CLEAR_STATE_NAME)) {
+        if (stateName.equals(OverdueWrapper.CLEAR_STATE_NAME)) {
             return clearState;
         }
         for (final DefaultOverdueState state : getStates()) {
@@ -52,7 +51,6 @@ public abstract class DefaultOverdueStateSet extends ValidatingConfig<DefaultOve
         throw new OverdueApiException(ErrorCode.CAT_NO_SUCH_OVEDUE_STATE, stateName);
     }
 
-
     /* (non-Javadoc)
      * @see org.killbill.billing.catalog.overdue.OverdueBillingState#findClearState()
      */
diff --git a/overdue/src/main/java/org/killbill/billing/overdue/listener/OverdueListener.java b/overdue/src/main/java/org/killbill/billing/overdue/listener/OverdueListener.java
index 8f1515f..47b4071 100644
--- a/overdue/src/main/java/org/killbill/billing/overdue/listener/OverdueListener.java
+++ b/overdue/src/main/java/org/killbill/billing/overdue/listener/OverdueListener.java
@@ -28,9 +28,12 @@ import org.killbill.billing.events.InvoiceAdjustmentInternalEvent;
 import org.killbill.billing.events.InvoicePaymentErrorInternalEvent;
 import org.killbill.billing.events.InvoicePaymentInfoInternalEvent;
 import org.killbill.billing.overdue.OverdueInternalApi;
+import org.killbill.billing.util.cache.Cachable.CacheType;
+import org.killbill.billing.util.cache.CacheControllerDispatcher;
 import org.killbill.billing.util.callcontext.CallOrigin;
 import org.killbill.billing.util.callcontext.InternalCallContextFactory;
 import org.killbill.billing.util.callcontext.UserType;
+import org.killbill.billing.util.dao.NonEntityDao;
 import org.killbill.billing.util.tag.ControlTagType;
 import org.killbill.bus.api.BusEvent;
 import org.slf4j.Logger;
@@ -46,28 +49,45 @@ public class OverdueListener {
 
     private final OverdueInternalApi overdueInternalApi;
     private final InternalCallContextFactory internalCallContextFactory;
+    private final CacheControllerDispatcher cacheControllerDispatcher;
+
+    private final NonEntityDao nonEntityDao;
 
     @Inject
     public OverdueListener(final OverdueInternalApi overdueInternalApi,
+                           final NonEntityDao nonEntityDao,
+                           final CacheControllerDispatcher cacheControllerDispatcher,
                            final InternalCallContextFactory internalCallContextFactory) {
         this.overdueInternalApi = overdueInternalApi;
+        this.nonEntityDao = nonEntityDao;
+        this.cacheControllerDispatcher = cacheControllerDispatcher;
         this.internalCallContextFactory = internalCallContextFactory;
     }
 
+
+
+
     @AllowConcurrentEvents
     @Subscribe
-    public void handle_OVERDUE_ENFORCEMENT_OFF_Insert(final ControlTagCreationInternalEvent event) {
+    public void handleTagInsert(final ControlTagCreationInternalEvent event) {
         if (event.getTagDefinition().getName().equals(ControlTagType.OVERDUE_ENFORCEMENT_OFF.toString()) && event.getObjectType() == ObjectType.ACCOUNT) {
             final InternalCallContext callContext = createCallContext(event.getUserToken(), event.getSearchKey1(), event.getSearchKey2());
             overdueInternalApi.scheduleOverdueClear(event.getObjectId(), callContext);
+        } else if (event.getTagDefinition().getName().equals(ControlTagType.WRITTEN_OFF.toString()) && event.getObjectType() == ObjectType.INVOICE) {
+            final UUID accountId = nonEntityDao.retrieveIdFromObject(event.getSearchKey1(), ObjectType.ACCOUNT, cacheControllerDispatcher.getCacheController(CacheType.OBJECT_ID));
+            insertBusEventIntoNotificationQueue(accountId, event);
         }
     }
 
+
     @AllowConcurrentEvents
     @Subscribe
-    public void handle_OVERDUE_ENFORCEMENT_OFF_Removal(final ControlTagDeletionInternalEvent event) {
+    public void handleTagRemoval(final ControlTagDeletionInternalEvent event) {
         if (event.getTagDefinition().getName().equals(ControlTagType.OVERDUE_ENFORCEMENT_OFF.toString()) && event.getObjectType() == ObjectType.ACCOUNT) {
             insertBusEventIntoNotificationQueue(event.getObjectId(), event);
+        } else if (event.getTagDefinition().getName().equals(ControlTagType.WRITTEN_OFF.toString()) && event.getObjectType() == ObjectType.INVOICE) {
+            final UUID accountId = nonEntityDao.retrieveIdFromObject(event.getSearchKey1(), ObjectType.ACCOUNT, cacheControllerDispatcher.getCacheController(CacheType.OBJECT_ID));
+            insertBusEventIntoNotificationQueue(accountId, event);
         }
     }
 
diff --git a/overdue/src/main/java/org/killbill/billing/overdue/wrapper/OverdueWrapper.java b/overdue/src/main/java/org/killbill/billing/overdue/wrapper/OverdueWrapper.java
index a7c14f8..95a2b7d 100644
--- a/overdue/src/main/java/org/killbill/billing/overdue/wrapper/OverdueWrapper.java
+++ b/overdue/src/main/java/org/killbill/billing/overdue/wrapper/OverdueWrapper.java
@@ -21,6 +21,7 @@ package org.killbill.billing.overdue.wrapper;
 import org.killbill.billing.account.api.ImmutableAccountData;
 import org.killbill.billing.callcontext.InternalCallContext;
 import org.killbill.billing.callcontext.InternalTenantContext;
+import org.killbill.billing.entitlement.api.BlockingState;
 import org.killbill.billing.entitlement.api.BlockingStateType;
 import org.killbill.billing.junction.BlockingInternalApi;
 import org.killbill.billing.overdue.OverdueService;
@@ -39,8 +40,13 @@ import org.killbill.commons.locker.LockFailedException;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import com.google.common.base.MoreObjects;
+
 public class OverdueWrapper {
 
+
+    public static final String CLEAR_STATE_NAME = "__KILLBILL__CLEAR__OVERDUE_STATE__";
+
     private static final Logger log = LoggerFactory.getLogger(OverdueWrapper.class);
 
     // Should we introduce a config?
@@ -93,8 +99,8 @@ public class OverdueWrapper {
 
     private OverdueState refreshWithLock(final InternalCallContext context) throws OverdueException, OverdueApiException {
         final BillingState billingState = billingState(context);
-        final String previousOverdueStateName = api.getBlockingStateForService(overdueable.getId(), BlockingStateType.ACCOUNT, OverdueService.OVERDUE_SERVICE_NAME, context).getStateName();
-
+        final BlockingState blockingStateForService = api.getBlockingStateForService(overdueable.getId(), BlockingStateType.ACCOUNT, OverdueService.OVERDUE_SERVICE_NAME, context);
+        final String previousOverdueStateName = blockingStateForService != null ? blockingStateForService.getStateName() : OverdueWrapper.CLEAR_STATE_NAME;
         final OverdueState currentOverdueState = overdueStateSet.findState(previousOverdueStateName);
         final OverdueState nextOverdueState = overdueStateSet.calculateOverdueState(billingState, clock.getToday(billingState.getAccountTimeZone()));
 
@@ -120,7 +126,8 @@ public class OverdueWrapper {
     }
 
     private void clearWithLock(final InternalCallContext context) throws OverdueException, OverdueApiException {
-        final String previousOverdueStateName = api.getBlockingStateForService(overdueable.getId(), BlockingStateType.ACCOUNT, OverdueService.OVERDUE_SERVICE_NAME, context).getStateName();
+        final BlockingState blockingStateForService = api.getBlockingStateForService(overdueable.getId(), BlockingStateType.ACCOUNT, OverdueService.OVERDUE_SERVICE_NAME, context);
+        final String previousOverdueStateName = blockingStateForService != null ? blockingStateForService.getStateName() : OverdueWrapper.CLEAR_STATE_NAME;
         final OverdueState previousOverdueState = overdueStateSet.findState(previousOverdueStateName);
         overdueStateApplicator.clear(overdueable, previousOverdueState, overdueStateSet.getClearState(), context);
     }
diff --git a/overdue/src/test/java/org/killbill/billing/overdue/applicator/TestOverdueStateApplicator.java b/overdue/src/test/java/org/killbill/billing/overdue/applicator/TestOverdueStateApplicator.java
index 5c77f5c..2222377 100644
--- a/overdue/src/test/java/org/killbill/billing/overdue/applicator/TestOverdueStateApplicator.java
+++ b/overdue/src/test/java/org/killbill/billing/overdue/applicator/TestOverdueStateApplicator.java
@@ -25,6 +25,7 @@ import java.util.concurrent.Callable;
 import org.killbill.billing.account.api.ImmutableAccountData;
 import org.killbill.billing.overdue.api.OverdueState;
 import org.killbill.billing.overdue.config.DefaultOverdueConfig;
+import org.killbill.billing.overdue.wrapper.OverdueWrapper;
 import org.mockito.Mockito;
 import org.testng.Assert;
 import org.testng.annotations.Test;
@@ -49,7 +50,7 @@ public class TestOverdueStateApplicator extends OverdueTestSuiteWithEmbeddedDB {
         Mockito.when(account.getId()).thenReturn(UUID.randomUUID());
 
         final OverdueStateSet overdueStateSet = config.getOverdueStatesAccount();
-        final OverdueState clearState = config.getOverdueStatesAccount().findState(DefaultBlockingState.CLEAR_STATE_NAME);
+        final OverdueState clearState = config.getOverdueStatesAccount().findState(OverdueWrapper.CLEAR_STATE_NAME);
         OverdueState state;
 
         state = config.getOverdueStatesAccount().findState("OD1");
diff --git a/overdue/src/test/java/org/killbill/billing/overdue/glue/TestOverdueModule.java b/overdue/src/test/java/org/killbill/billing/overdue/glue/TestOverdueModule.java
index 142f39d..df5a469 100644
--- a/overdue/src/test/java/org/killbill/billing/overdue/glue/TestOverdueModule.java
+++ b/overdue/src/test/java/org/killbill/billing/overdue/glue/TestOverdueModule.java
@@ -21,6 +21,7 @@ package org.killbill.billing.overdue.glue;
 import java.util.List;
 import java.util.UUID;
 
+import org.joda.time.DateTime;
 import org.killbill.billing.callcontext.InternalCallContext;
 import org.killbill.billing.callcontext.InternalTenantContext;
 import org.killbill.billing.entitlement.api.BlockingState;
@@ -37,6 +38,7 @@ import org.killbill.billing.overdue.applicator.OverdueBusListenerTester;
 import org.killbill.billing.overdue.caching.MockOverdueConfigCache;
 import org.killbill.billing.overdue.caching.OverdueCacheInvalidationCallback;
 import org.killbill.billing.overdue.caching.OverdueConfigCache;
+import org.killbill.billing.overdue.wrapper.OverdueWrapper;
 import org.killbill.billing.platform.api.KillbillConfigSource;
 import org.killbill.billing.tenant.api.TenantInternalApi.CacheInvalidationCallback;
 import org.killbill.billing.util.email.EmailModule;
@@ -46,6 +48,7 @@ import org.killbill.billing.util.glue.CacheModule;
 import org.killbill.billing.util.glue.CallContextModule;
 import org.killbill.billing.util.glue.CustomFieldModule;
 import org.killbill.billing.util.glue.MemoryGlobalLockerModule;
+import org.killbill.clock.Clock;
 import org.killbill.clock.ClockMock;
 
 import com.google.inject.name.Names;
@@ -90,12 +93,13 @@ public class TestOverdueModule extends DefaultOverdueModule {
             return blockingState;
         }
 
+
         @Override
         public BlockingState getBlockingStateForService(final UUID blockableId, final BlockingStateType blockingStateType, final String serviceName, final InternalTenantContext context) {
             if (blockingState != null && blockingState.getBlockedId().equals(blockableId)) {
                 return blockingState;
             } else {
-                return DefaultBlockingState.getClearState(blockingStateType, serviceName, new ClockMock());
+                return new DefaultBlockingState(null, blockingStateType, OverdueWrapper.CLEAR_STATE_NAME, serviceName, false, false, false, null);
             }
         }
 
diff --git a/overdue/src/test/java/org/killbill/billing/overdue/wrapper/TestOverdueWrapper.java b/overdue/src/test/java/org/killbill/billing/overdue/wrapper/TestOverdueWrapper.java
index c6e2231..2c22056 100644
--- a/overdue/src/test/java/org/killbill/billing/overdue/wrapper/TestOverdueWrapper.java
+++ b/overdue/src/test/java/org/killbill/billing/overdue/wrapper/TestOverdueWrapper.java
@@ -76,7 +76,7 @@ public class TestOverdueWrapper extends OverdueTestSuiteWithEmbeddedDB {
 
         final InputStream is = new ByteArrayInputStream(testOverdueHelper.getConfigXml().getBytes());
         final DefaultOverdueConfig config = XMLLoader.getObjectFromStreamNoValidation(is, DefaultOverdueConfig.class);
-        state = config.getOverdueStatesAccount().findState(DefaultBlockingState.CLEAR_STATE_NAME);
+        state = config.getOverdueStatesAccount().findState(OverdueWrapper.CLEAR_STATE_NAME);
         account = testOverdueHelper.createImmutableAccountData(clock.getUTCToday().minusDays(31));
         wrapper = overdueWrapperFactory.createOverdueWrapperFor(account, internalCallContext);
         final OverdueState result = wrapper.refresh(internalCallContext);
diff --git a/payment/src/main/java/org/killbill/billing/payment/core/sm/payments/ChargebackOperation.java b/payment/src/main/java/org/killbill/billing/payment/core/sm/payments/ChargebackOperation.java
index a2993a6..cf477d5 100644
--- a/payment/src/main/java/org/killbill/billing/payment/core/sm/payments/ChargebackOperation.java
+++ b/payment/src/main/java/org/killbill/billing/payment/core/sm/payments/ChargebackOperation.java
@@ -17,14 +17,11 @@
 
 package org.killbill.billing.payment.core.sm.payments;
 
-import java.math.BigDecimal;
-
 import org.killbill.automaton.OperationResult;
 import org.killbill.billing.payment.api.PaymentApiException;
 import org.killbill.billing.payment.api.TransactionType;
 import org.killbill.billing.payment.core.sm.PaymentAutomatonDAOHelper;
 import org.killbill.billing.payment.core.sm.PaymentStateContext;
-import org.killbill.billing.payment.dao.PaymentTransactionModelDao;
 import org.killbill.billing.payment.dispatcher.PluginDispatcher;
 import org.killbill.billing.payment.plugin.api.PaymentPluginApiException;
 import org.killbill.billing.payment.plugin.api.PaymentPluginStatus;
@@ -50,28 +47,6 @@ public class ChargebackOperation extends PaymentOperation {
     @Override
     protected PaymentTransactionInfoPlugin doCallSpecificOperationCallback() throws PaymentPluginApiException {
         logger.debug("Starting CHARGEBACK for payment {} ({} {})", paymentStateContext.getPaymentId(), paymentStateContext.getAmount(), paymentStateContext.getCurrency());
-
-        final PaymentPluginStatus status;
-        if (!paymentStateContext.getOnLeavingStateExistingTransactions().isEmpty()) {
-            final Iterable<PaymentTransactionModelDao> purchaseTransactions = getOnLeavingStateExistingTransactionsForType(TransactionType.PURCHASE);
-            final Iterable<PaymentTransactionModelDao> captureTransactions = getOnLeavingStateExistingTransactionsForType(TransactionType.CAPTURE);
-            final Iterable<PaymentTransactionModelDao> refundTransactions = getOnLeavingStateExistingTransactionsForType(TransactionType.REFUND);
-            final Iterable<PaymentTransactionModelDao> chargebackTransactions = getOnLeavingStateExistingTransactionsForType(TransactionType.CHARGEBACK);
-
-            final BigDecimal purchasedAmount = getSumAmount(purchaseTransactions);
-            final BigDecimal capturedAmount = getSumAmount(captureTransactions);
-            final BigDecimal refundedAmount = getSumAmount(refundTransactions);
-            final BigDecimal chargebackAmount = getSumAmount(chargebackTransactions);
-            final BigDecimal chargebackAvailableAmount = purchasedAmount.add(capturedAmount).subtract(refundedAmount.add(chargebackAmount));
-
-            if (paymentStateContext.getAmount().compareTo(chargebackAvailableAmount) > 0) {
-                status = PaymentPluginStatus.ERROR;
-            } else {
-                status = PaymentPluginStatus.PROCESSED;
-            }
-        } else {
-            status = PaymentPluginStatus.PROCESSED;
-        }
         return new DefaultNoOpPaymentInfoPlugin(paymentStateContext.getPaymentId(),
                                                 paymentStateContext.getTransactionId(),
                                                 TransactionType.CHARGEBACK,
@@ -79,7 +54,7 @@ public class ChargebackOperation extends PaymentOperation {
                                                 paymentStateContext.getCurrency(),
                                                 null,
                                                 null,
-                                                status,
+                                                PaymentPluginStatus.PROCESSED,
                                                 null,
                                                 null);
     }
diff --git a/payment/src/main/java/org/killbill/billing/payment/dispatcher/CallableWithRequestData.java b/payment/src/main/java/org/killbill/billing/payment/dispatcher/CallableWithRequestData.java
index b5be8c5..d8012d2 100644
--- a/payment/src/main/java/org/killbill/billing/payment/dispatcher/CallableWithRequestData.java
+++ b/payment/src/main/java/org/killbill/billing/payment/dispatcher/CallableWithRequestData.java
@@ -1,6 +1,6 @@
 /*
- * Copyright 2014-2015 Groupon, Inc
- * Copyright 2014-2015 The Billing Project, LLC
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 The Billing Project, LLC
  *
  * The Billing Project licenses this file to you under the Apache License, version 2.0
  * (the "License"); you may not use this file except in compliance with the
@@ -17,23 +17,27 @@
 
 package org.killbill.billing.payment.dispatcher;
 
+import java.util.Random;
 import java.util.concurrent.Callable;
 
 import org.apache.shiro.mgt.SecurityManager;
 import org.apache.shiro.subject.Subject;
 import org.apache.shiro.util.ThreadContext;
+import org.killbill.billing.util.UUIDs;
 import org.killbill.commons.request.Request;
 import org.killbill.commons.request.RequestData;
 
 public class CallableWithRequestData<T> implements Callable<T> {
 
     private final RequestData requestData;
+    private final Random random;
     private final SecurityManager securityManager;
     private final Subject subject;
     private final Callable<T> delegate;
 
-    public CallableWithRequestData(final RequestData requestData, final SecurityManager securityManager, final Subject subject, final Callable<T> delegate) {
+    public CallableWithRequestData(final RequestData requestData, final Random random, final SecurityManager securityManager, final Subject subject, final Callable<T> delegate) {
         this.requestData = requestData;
+        this.random = random;
         this.securityManager = securityManager;
         this.subject = subject;
         this.delegate = delegate;
@@ -43,11 +47,13 @@ public class CallableWithRequestData<T> implements Callable<T> {
     public T call() throws Exception {
         try {
             Request.setPerThreadRequestData(requestData);
+            UUIDs.setRandom(random);
             ThreadContext.bind(securityManager);
             ThreadContext.bind(subject);
             return delegate.call();
         } finally {
             Request.resetPerThreadRequestData();
+            UUIDs.setRandom(null);
             ThreadContext.unbindSecurityManager();
             ThreadContext.unbindSubject();
         }
diff --git a/payment/src/main/java/org/killbill/billing/payment/dispatcher/PluginDispatcher.java b/payment/src/main/java/org/killbill/billing/payment/dispatcher/PluginDispatcher.java
index a457e17..fa65e1b 100644
--- a/payment/src/main/java/org/killbill/billing/payment/dispatcher/PluginDispatcher.java
+++ b/payment/src/main/java/org/killbill/billing/payment/dispatcher/PluginDispatcher.java
@@ -1,7 +1,7 @@
 /*
  * Copyright 2010-2013 Ning, Inc.
- * Copyright 2014 Groupon, Inc
- * Copyright 2014 The Billing Project, LLC
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 The Billing Project, LLC
  *
  * The Billing Project licenses this file to you under the Apache License, version 2.0
  * (the "License"); you may not use this file except in compliance with the
@@ -27,6 +27,7 @@ import java.util.concurrent.TimeoutException;
 
 import org.apache.shiro.util.ThreadContext;
 import org.killbill.billing.payment.core.PaymentExecutors;
+import org.killbill.billing.util.UUIDs;
 import org.killbill.commons.profiling.Profiling;
 import org.killbill.commons.profiling.ProfilingData;
 import org.killbill.commons.request.Request;
@@ -57,7 +58,11 @@ public class PluginDispatcher<ReturnType> {
         final ExecutorService pluginExecutor = paymentExecutors.getPluginExecutorService();
 
         // Wrap existing callable to keep the original requestId
-        final Callable<PluginDispatcherReturnType<ReturnType>> callableWithRequestData = new CallableWithRequestData(Request.getPerThreadRequestData(), ThreadContext.getSecurityManager(), ThreadContext.getSubject(), task);
+        final Callable<PluginDispatcherReturnType<ReturnType>> callableWithRequestData = new CallableWithRequestData(Request.getPerThreadRequestData(),
+                                                                                                                     UUIDs.getRandom(),
+                                                                                                                     ThreadContext.getSecurityManager(),
+                                                                                                                     ThreadContext.getSubject(),
+                                                                                                                     task);
 
         final Future<PluginDispatcherReturnType<ReturnType>> future = pluginExecutor.submit(callableWithRequestData);
         final PluginDispatcherReturnType<ReturnType> pluginDispatcherResult = future.get(timeout, unit);
diff --git a/payment/src/test/java/org/killbill/billing/payment/core/sm/TestPluginOperation.java b/payment/src/test/java/org/killbill/billing/payment/core/sm/TestPluginOperation.java
index 650bd00..41155e8 100644
--- a/payment/src/test/java/org/killbill/billing/payment/core/sm/TestPluginOperation.java
+++ b/payment/src/test/java/org/killbill/billing/payment/core/sm/TestPluginOperation.java
@@ -1,8 +1,8 @@
 /*
- * Copyright 2014 Groupon, Inc
- * Copyright 2014 The Billing Project, LLC
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 The Billing Project, LLC
  *
- * Groupon licenses this file to you under the Apache License, version 2.0
+ * The Billing Project licenses this file to you under the Apache License, version 2.0
  * (the "License"); you may not use this file except in compliance with the
  * License.  You may obtain a copy of the License at:
  *
@@ -21,6 +21,7 @@ import java.math.BigDecimal;
 import java.util.UUID;
 import java.util.concurrent.Callable;
 import java.util.concurrent.Semaphore;
+import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.concurrent.atomic.AtomicInteger;
 
@@ -45,8 +46,6 @@ import org.killbill.billing.util.config.PaymentConfig;
 import org.killbill.commons.locker.GlobalLocker;
 import org.killbill.commons.locker.memory.MemoryGlobalLocker;
 import org.mockito.Mockito;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
 import org.testng.Assert;
 import org.testng.annotations.BeforeMethod;
 import org.testng.annotations.Test;
@@ -56,13 +55,13 @@ import com.jayway.awaitility.Awaitility;
 
 public class TestPluginOperation extends PaymentTestSuiteNoDB {
 
-    private final static String PLUGIN_NAME_PLACEHOLDER = "pluginName";
+    private static final String PLUGIN_NAME_PLACEHOLDER = "pluginName";
+
+    private static final int TIMEOUT = 5;
 
     private final GlobalLocker locker = new MemoryGlobalLocker();
     private final Account account = Mockito.mock(Account.class);
 
-    private static final Logger logger = LoggerFactory.getLogger(TestPluginOperation.class);
-
     @BeforeMethod(groups = "fast")
     public void beforeMethod() throws Exception {
         super.beforeMethod();
@@ -70,11 +69,10 @@ public class TestPluginOperation extends PaymentTestSuiteNoDB {
     }
 
     @Test(groups = "fast")
-    public void testWithAccountLock() throws Exception {
-        testLocking(true);
+    public void testAccountLock() throws Exception {
+        testLocking();
     }
 
-
     @Test(groups = "fast")
     public void testOperationThrowsPaymentApiException() throws Exception {
         final CallbackTest callback = new CallbackTest(new PaymentApiException(ErrorCode.__UNKNOWN_ERROR_CODE));
@@ -103,30 +101,31 @@ public class TestPluginOperation extends PaymentTestSuiteNoDB {
         }
     }
 
-    private void testLocking(final boolean withAccountLock) throws Exception {
+    private void testLocking() throws Exception {
         final Semaphore available = new Semaphore(1, true);
         final CallbackTest callback = new CallbackTest(available);
-        final PaymentOperation pluginOperation = getPluginOperation(withAccountLock);
+        final PaymentOperation pluginOperation = getPluginOperation(true);
 
         // Take the only permit
         available.acquire();
 
         // Start the plugin operation in the background (will block)
         runPluginOperationInBackground(pluginOperation, callback, false);
+        Awaitility.await()
+                  .until(new Callable<Boolean>() {
+                      @Override
+                      public Boolean call() throws Exception {
+                          return callback.getStartCount() == 1;
+                      }
+                  });
 
         // The operation should be blocked here because we have the semaphore
-        Assert.assertEquals(available.getQueueLength(), 1);
         Assert.assertEquals(callback.getRunCount(), 0);
 
-        if (withAccountLock) {
-            // If the account is locked, trying to run the operation again will throw LockFailedException
-            runPluginOperationInBackground(pluginOperation, callback, true);
-        } else {
-            // If the account is not locked, it will just block
-            runPluginOperationInBackground(pluginOperation, callback, false);
-            Assert.assertEquals(available.getQueueLength(), 2);
-            Assert.assertEquals(callback.getRunCount(), 0);
-        }
+        // Trying to run the operation again will throw LockFailedException
+        Awaitility.await().atMost(2 * TIMEOUT, TimeUnit.SECONDS).untilTrue(runPluginOperationInBackground(pluginOperation, callback, true));
+
+        Assert.assertEquals(callback.getRunCount(), 0);
 
         // Release the semaphore
         available.release();
@@ -136,37 +135,41 @@ public class TestPluginOperation extends PaymentTestSuiteNoDB {
                   .until(new Callable<Boolean>() {
                       @Override
                       public Boolean call() throws Exception {
-                          return callback.getRunCount() == (withAccountLock ? 1 : 2);
+                          return callback.getRunCount() == 1;
                       }
                   });
 
         // Verify the final state
-        Assert.assertEquals(available.getQueueLength(), 0);
-        Assert.assertEquals(callback.getRunCount(), withAccountLock ? 1 : 2);
+        Assert.assertEquals(available.availablePermits(), 1);
     }
 
-    private void runPluginOperationInBackground(final PaymentOperation pluginOperation, final CallbackTest callback, final boolean shouldFailBecauseOfLockFailure) throws Exception {
-        final AtomicBoolean threadStarted = new AtomicBoolean(false);
+    private AtomicBoolean runPluginOperationInBackground(final PaymentOperation pluginOperation, final CallbackTest callback, final boolean shouldFailBecauseOfLockFailure) throws Exception {
+        final AtomicBoolean threadRunning = new AtomicBoolean(false);
+        final AtomicBoolean threadHasRun = new AtomicBoolean(false);
         final Thread t1 = new Thread(new Runnable() {
             @Override
             public void run() {
-                threadStarted.set(true);
-
-                if (shouldFailBecauseOfLockFailure) {
-                    try {
-                        pluginOperation.dispatchWithAccountLockAndTimeout(PLUGIN_NAME_PLACEHOLDER, callback);
-                        Assert.fail();
-                    } catch (final OperationException e) {
-                        Assert.assertTrue(e.getCause() instanceof PaymentApiException);
-                        // No better error code for lock failures...
-                        Assert.assertEquals(((PaymentApiException) e.getCause()).getCode(), ErrorCode.PAYMENT_INTERNAL_ERROR.getCode());
-                    }
-                } else {
-                    try {
-                        pluginOperation.dispatchWithAccountLockAndTimeout(PLUGIN_NAME_PLACEHOLDER, callback);
-                    } catch (final OperationException e) {
-                        Assert.fail(e.getMessage());
+                threadRunning.set(true);
+
+                try {
+                    if (shouldFailBecauseOfLockFailure) {
+                        try {
+                            pluginOperation.dispatchWithAccountLockAndTimeout(PLUGIN_NAME_PLACEHOLDER, callback);
+                            Assert.fail();
+                        } catch (final OperationException e) {
+                            Assert.assertTrue(e.getCause() instanceof PaymentApiException);
+                            // No better error code for lock failures...
+                            Assert.assertEquals(((PaymentApiException) e.getCause()).getCode(), ErrorCode.PAYMENT_INTERNAL_ERROR.getCode());
+                        }
+                    } else {
+                        try {
+                            pluginOperation.dispatchWithAccountLockAndTimeout(PLUGIN_NAME_PLACEHOLDER, callback);
+                        } catch (final OperationException e) {
+                            Assert.fail(e.getMessage());
+                        }
                     }
+                } finally {
+                    threadHasRun.set(true);
                 }
             }
         });
@@ -174,7 +177,9 @@ public class TestPluginOperation extends PaymentTestSuiteNoDB {
         t1.start();
 
         // Make sure the thread has started
-        Awaitility.await().untilTrue(threadStarted);
+        Awaitility.await().untilTrue(threadRunning);
+
+        return threadHasRun;
     }
 
     private PaymentOperation getPluginOperation() throws PaymentApiException {
@@ -182,7 +187,7 @@ public class TestPluginOperation extends PaymentTestSuiteNoDB {
     }
 
     private PaymentOperation getPluginOperation(final boolean shouldLockAccount) throws PaymentApiException {
-        return getPluginOperation(shouldLockAccount, Integer.MAX_VALUE);
+        return getPluginOperation(shouldLockAccount, TIMEOUT);
     }
 
     private PaymentOperation getPluginOperation(final boolean shouldLockAccount, final int timeoutSeconds) throws PaymentApiException {
@@ -209,6 +214,7 @@ public class TestPluginOperation extends PaymentTestSuiteNoDB {
 
     private static final class CallbackTest implements DispatcherCallback<PluginDispatcherReturnType<OperationResult>, PaymentApiException> {
 
+        private final AtomicInteger startCount = new AtomicInteger(0);
         private final AtomicInteger runCount = new AtomicInteger(0);
 
         private final Semaphore available;
@@ -242,9 +248,11 @@ public class TestPluginOperation extends PaymentTestSuiteNoDB {
 
         @Override
         public PluginDispatcherReturnType<OperationResult> doOperation() throws PaymentApiException {
+            startCount.incrementAndGet();
+
             try {
                 if (available != null) {
-                    available.acquire();
+                    available.acquireUninterruptibly();
                 }
 
                 if (sleepTimeMillis != null) {
@@ -272,6 +280,10 @@ public class TestPluginOperation extends PaymentTestSuiteNoDB {
         public int getRunCount() {
             return runCount.get();
         }
+
+        public int getStartCount() {
+            return startCount.get();
+        }
     }
 
     private static final class PluginOperationTest extends PaymentOperation {
diff --git a/payment/src/test/java/org/killbill/billing/payment/dispatcher/TestPluginDispatcher.java b/payment/src/test/java/org/killbill/billing/payment/dispatcher/TestPluginDispatcher.java
index 06077cc..f2cd9c8 100644
--- a/payment/src/test/java/org/killbill/billing/payment/dispatcher/TestPluginDispatcher.java
+++ b/payment/src/test/java/org/killbill/billing/payment/dispatcher/TestPluginDispatcher.java
@@ -1,7 +1,9 @@
 /*
  * Copyright 2010-2013 Ning, Inc.
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 The Billing Project, LLC
  *
- * Ning licenses this file to you under the Apache License, version 2.0
+ * The Billing Project licenses this file to you under the Apache License, version 2.0
  * (the "License"); you may not use this file except in compliance with the
  * License.  You may obtain a copy of the License at:
  *
@@ -25,6 +27,7 @@ import org.killbill.billing.ErrorCode;
 import org.killbill.billing.payment.PaymentTestSuiteNoDB;
 import org.killbill.billing.payment.api.PaymentApiException;
 import org.killbill.billing.payment.dispatcher.PluginDispatcher.PluginDispatcherReturnType;
+import org.killbill.billing.util.UUIDs;
 import org.killbill.commons.request.Request;
 import org.killbill.commons.request.RequestData;
 import org.testng.Assert;
@@ -134,6 +137,7 @@ public class TestPluginDispatcher extends PaymentTestSuiteNoDB {
         };
 
         final CallableWithRequestData<PluginDispatcherReturnType<String>> callable = new CallableWithRequestData<PluginDispatcherReturnType<String>>(new RequestData(requestId),
+                                                                                                                                                     UUIDs.getRandom(),
                                                                                                                                                      null,
                                                                                                                                                      null,
                                                                                                                                                      delegate);
diff --git a/payment/src/test/java/org/killbill/billing/payment/glue/MockEntitlementModuleForPayment.java b/payment/src/test/java/org/killbill/billing/payment/glue/MockEntitlementModuleForPayment.java
index ae2c6aa..46a82ab 100644
--- a/payment/src/test/java/org/killbill/billing/payment/glue/MockEntitlementModuleForPayment.java
+++ b/payment/src/test/java/org/killbill/billing/payment/glue/MockEntitlementModuleForPayment.java
@@ -43,7 +43,7 @@ public class MockEntitlementModuleForPayment extends MockEntitlementModule {
     public void installBlockingApi() {
         super.installBlockingApi();
 
-        final BlockingState blockingState = new DefaultBlockingState(null, BlockingStateType.ACCOUNT, DefaultBlockingState.CLEAR_STATE_NAME, "test", false, false, false, new DateTime(DateTimeZone.UTC));
+        final BlockingState blockingState = new DefaultBlockingState(null, BlockingStateType.ACCOUNT, "CLEAR_STATE_NAME", "test", false, false, false, new DateTime(DateTimeZone.UTC));
         Mockito.when(blockingApi.getBlockingAllForAccount(Mockito.<InternalTenantContext>any())).thenReturn(ImmutableList.<BlockingState>of(blockingState));
         Mockito.when(blockingApi.getBlockingStateForService(Mockito.<UUID>any(), Mockito.<BlockingStateType>any(), Mockito.anyString(), Mockito.<InternalTenantContext>any())).thenReturn(blockingState);
     }
diff --git a/payment/src/test/java/org/killbill/billing/payment/TestJanitor.java b/payment/src/test/java/org/killbill/billing/payment/TestJanitor.java
index f62e802..0ba6343 100644
--- a/payment/src/test/java/org/killbill/billing/payment/TestJanitor.java
+++ b/payment/src/test/java/org/killbill/billing/payment/TestJanitor.java
@@ -80,6 +80,8 @@ import static org.testng.Assert.fail;
 
 public class TestJanitor extends PaymentTestSuiteWithEmbeddedDB {
 
+    private static final int TIMEOUT = 10;
+
     final PaymentOptions INVOICE_PAYMENT = new PaymentOptions() {
         @Override
         public boolean isExternalPayment() {
@@ -205,7 +207,7 @@ public class TestJanitor extends PaymentTestSuiteWithEmbeddedDB {
     }
 
     @Test(groups = "slow")
-    public void testCreateSuccessRefundPaymentControlWithItemAdjustments() throws PaymentApiException, InvoiceApiException, EventBusException {
+    public void testCreateSuccessRefundPaymentControlWithItemAdjustments() throws Exception {
 
         final BigDecimal requestedAmount = BigDecimal.TEN;
         final UUID subscriptionId = UUID.randomUUID();
@@ -263,13 +265,14 @@ public class TestJanitor extends PaymentTestSuiteWithEmbeddedDB {
         assertEquals(attempt2.getStateName(), "INIT");
 
         clock.addDays(1);
-        try {
-            Thread.sleep(1500);
-        } catch (InterruptedException e) {
-        }
 
-        final PaymentAttemptModelDao attempt3 = paymentDao.getPaymentAttempt(refundAttempt.getId(), internalCallContext);
-        assertEquals(attempt3.getStateName(), "SUCCESS");
+        await().atMost(TIMEOUT, TimeUnit.SECONDS).until(new Callable<Boolean>() {
+            @Override
+            public Boolean call() throws Exception {
+                final PaymentAttemptModelDao attempt3 = paymentDao.getPaymentAttempt(refundAttempt.getId(), internalCallContext);
+                return "SUCCESS".equals(attempt3.getStateName());
+            }
+        });
     }
 
     @Test(groups = "slow")
@@ -418,7 +421,7 @@ public class TestJanitor extends PaymentTestSuiteWithEmbeddedDB {
         Assert.assertEquals(updatedPayment.getTransactions().get(0).getTransactionStatus(), TransactionStatus.SUCCESS);
     }
 
-    // The test will check that when a PENDING entry stays PENDING, we go through all our retries and evebtually give up (no infinite loop of retries)
+    // The test will check that when a PENDING entry stays PENDING, we go through all our retries and eventually give up (no infinite loop of retries)
     @Test(groups = "slow")
     public void testPendingEntriesThatDontMove() throws Exception {
 
@@ -450,23 +453,24 @@ public class TestJanitor extends PaymentTestSuiteWithEmbeddedDB {
         testListener.assertListenerStatus();
 
         // 15s,1m,3m,1h,1d,1d,1d,1d,1d
-        for (TimeSpan cur : paymentConfig.getIncompleteTransactionsRetries()) {
+        for (final TimeSpan cur : paymentConfig.getIncompleteTransactionsRetries()) {
             // Verify there is a notification to retry updating the value
             assertEquals(getPendingNotificationCnt(internalCallContext), 1);
 
             clock.addDeltaFromReality(cur.getMillis() + 1);
 
-            // We add a sleep here to make sure the notification gets processed. Note that calling assertNotificationsCompleted would not work
-            // because there is a point in time where the notification  queue is empty (showing notification was processed), but the processing of the notification
+            assertNotificationsCompleted(internalCallContext, 5);
+            // We add a sleep here to make sure the notification gets processed. Note that calling assertNotificationsCompleted alone would not work
+            // because there is a point in time where the notification queue is empty (showing notification was processed), but the processing of the notification
             // will itself enter a new notification, and so the synchronization is difficult without writing *too much code*.
             Thread.sleep(1000);
+            assertNotificationsCompleted(internalCallContext, 5);
 
-            //assertNotificationsCompleted(internalCallContext, 5);
             final Payment updatedPayment = paymentApi.getPayment(payment.getId(), false, ImmutableList.<PluginProperty>of(), callContext);
             Assert.assertEquals(updatedPayment.getTransactions().get(0).getTransactionStatus(), TransactionStatus.PENDING);
         }
 
-        await().atMost(5, TimeUnit.SECONDS).until(new Callable<Boolean>() {
+        await().atMost(TIMEOUT, TimeUnit.SECONDS).until(new Callable<Boolean>() {
             @Override
             public Boolean call() throws Exception {
                 return getPendingNotificationCnt(internalCallContext) == 0;
@@ -515,8 +519,12 @@ public class TestJanitor extends PaymentTestSuiteWithEmbeddedDB {
             await().atMost(timeoutSec, SECONDS).until(new Callable<Boolean>() {
                 @Override
                 public Boolean call() throws Exception {
-                    final List<NotificationEventWithMetadata<NotificationEvent>> notifications = notificationQueueService.getNotificationQueue(DefaultPaymentService.SERVICE_NAME, Janitor.QUEUE_NAME).getFutureOrInProcessingNotificationForSearchKeys(internalCallContext.getAccountRecordId(), internalCallContext.getTenantRecordId());
-                    return notifications.isEmpty();
+                    for (final NotificationEventWithMetadata<NotificationEvent> notificationEvent : notificationQueueService.getNotificationQueue(DefaultPaymentService.SERVICE_NAME, Janitor.QUEUE_NAME).getFutureOrInProcessingNotificationForSearchKeys(internalCallContext.getAccountRecordId(), internalCallContext.getTenantRecordId())) {
+                        if (!notificationEvent.getEffectiveDate().isAfter(clock.getUTCNow())) {
+                            return false;
+                        }
+                    }
+                    return true;
                 }
             });
         } catch (final Exception e) {
diff --git a/payment/src/test/java/org/killbill/billing/payment/TestRetryService.java b/payment/src/test/java/org/killbill/billing/payment/TestRetryService.java
index cbc23e5..f289f57 100644
--- a/payment/src/test/java/org/killbill/billing/payment/TestRetryService.java
+++ b/payment/src/test/java/org/killbill/billing/payment/TestRetryService.java
@@ -58,6 +58,8 @@ import static org.testng.Assert.fail;
 
 public class TestRetryService extends PaymentTestSuiteNoDB {
 
+    private static final int TIMEOUT = 10;
+
     private MockPaymentProviderPlugin mockPaymentProviderPlugin;
 
     @Override
@@ -174,7 +176,7 @@ public class TestRetryService extends PaymentTestSuiteNoDB {
         moveClockForFailureType(FailureType.PAYMENT_FAILURE, 0);
 
         try {
-            await().atMost(5, SECONDS).until(new Callable<Boolean>() {
+            await().atMost(TIMEOUT, SECONDS).until(new Callable<Boolean>() {
                 @Override
                 public Boolean call() throws Exception {
                     final List<PaymentAttemptModelDao> attempts = paymentDao.getPaymentAttempts(paymentExternalKey, internalCallContext);
@@ -260,7 +262,7 @@ public class TestRetryService extends PaymentTestSuiteNoDB {
             final int curFailureCondition = curFailure;
 
             try {
-                await().atMost(5, SECONDS).until(new Callable<Boolean>() {
+                await().atMost(TIMEOUT, SECONDS).until(new Callable<Boolean>() {
                     @Override
                     public Boolean call() throws Exception {
                         final List<PaymentAttemptModelDao> attempts = paymentDao.getPaymentAttempts(paymentExternalKey, internalCallContext);
@@ -345,7 +347,7 @@ public class TestRetryService extends PaymentTestSuiteNoDB {
             final int curFailureCondition = curFailure;
 
             try {
-                await().atMost(5, SECONDS).until(new Callable<Boolean>() {
+                await().atMost(TIMEOUT, SECONDS).until(new Callable<Boolean>() {
                     @Override
                     public Boolean call() throws Exception {
                         final List<PaymentAttemptModelDao> attempts = paymentDao.getPaymentAttempts(paymentExternalKey, internalCallContext);
diff --git a/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/KillbillClient.java b/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/KillbillClient.java
index 5b498cf..d8ed6a8 100644
--- a/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/KillbillClient.java
+++ b/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/KillbillClient.java
@@ -40,7 +40,7 @@ import static org.testng.Assert.assertNotNull;
 
 public abstract class KillbillClient extends GuicyKillbillTestSuiteWithEmbeddedDB {
 
-    protected final int DEFAULT_WAIT_COMPLETION_TIMEOUT_SEC = 5;
+    protected final int DEFAULT_WAIT_COMPLETION_TIMEOUT_SEC = 10;
 
     protected static final String PLUGIN_NAME = "noop";
 
diff --git a/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestPushNotification.java b/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestPushNotification.java
index 9184c07..390e4ed 100644
--- a/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestPushNotification.java
+++ b/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestPushNotification.java
@@ -71,7 +71,7 @@ public class TestPushNotification extends TestJaxrsBase {
     }
 
     private boolean waitForCallbacksToComplete() throws InterruptedException {
-        long remainingMs = 20000;
+        long remainingMs = 30000;
         do {
             if (callbackCompleted) {
                 break;