killbill-aplcache

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

2/9/2016 5:20:52 PM

Changes

account/pom.xml 2(+1 -1)

api/pom.xml 2(+1 -1)

beatrix/pom.xml 2(+1 -1)

catalog/pom.xml 2(+1 -1)

currency/pom.xml 2(+1 -1)

invoice/pom.xml 2(+1 -1)

jaxrs/pom.xml 2(+1 -1)

junction/pom.xml 2(+1 -1)

NEWS 3(+3 -0)

overdue/pom.xml 2(+1 -1)

payment/pom.xml 2(+1 -1)

pom.xml 2(+1 -1)

profiles/pom.xml 2(+1 -1)

tenant/pom.xml 2(+1 -1)

usage/pom.xml 2(+1 -1)

util/pom.xml 2(+1 -1)

Details

account/pom.xml 2(+1 -1)

diff --git a/account/pom.xml b/account/pom.xml
index d28390d..b876bcd 100644
--- a/account/pom.xml
+++ b/account/pom.xml
@@ -19,7 +19,7 @@
     <parent>
         <artifactId>killbill</artifactId>
         <groupId>org.kill-bill.billing</groupId>
-        <version>0.16.1-SNAPSHOT</version>
+        <version>0.16.2-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>killbill-account</artifactId>
diff --git a/account/src/main/java/org/killbill/billing/account/api/DefaultChangedField.java b/account/src/main/java/org/killbill/billing/account/api/DefaultChangedField.java
index 7ee0e32..8b11cfb 100644
--- a/account/src/main/java/org/killbill/billing/account/api/DefaultChangedField.java
+++ b/account/src/main/java/org/killbill/billing/account/api/DefaultChangedField.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:
  *
@@ -17,7 +19,6 @@
 package org.killbill.billing.account.api;
 
 import org.joda.time.DateTime;
-
 import org.killbill.billing.events.ChangedField;
 
 import com.fasterxml.jackson.annotation.JsonCreator;
@@ -41,12 +42,6 @@ public class DefaultChangedField implements ChangedField {
         this.newValue = newValue;
     }
 
-    public DefaultChangedField(final String fieldName,
-                               final String oldValue,
-                               final String newValue) {
-        this(fieldName, oldValue, newValue, new DateTime());
-    }
-
     @Override
     public String getFieldName() {
         return fieldName;
@@ -124,5 +119,4 @@ public class DefaultChangedField implements ChangedField {
         }
         return true;
     }
-
 }
diff --git a/account/src/main/java/org/killbill/billing/account/api/user/DefaultAccountChangeEvent.java b/account/src/main/java/org/killbill/billing/account/api/user/DefaultAccountChangeEvent.java
index 9ee7111..d68f0dc 100644
--- a/account/src/main/java/org/killbill/billing/account/api/user/DefaultAccountChangeEvent.java
+++ b/account/src/main/java/org/killbill/billing/account/api/user/DefaultAccountChangeEvent.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:
  *
@@ -17,9 +19,11 @@
 package org.killbill.billing.account.api.user;
 
 import java.util.ArrayList;
+import java.util.Collection;
 import java.util.List;
 import java.util.UUID;
 
+import org.joda.time.DateTime;
 import org.killbill.billing.account.api.DefaultChangedField;
 import org.killbill.billing.account.dao.AccountModelDao;
 import org.killbill.billing.events.AccountChangeInternalEvent;
@@ -47,10 +51,16 @@ public class DefaultAccountChangeEvent extends BusEventBase implements AccountCh
         this.changedFields = changedFields;
     }
 
-    public DefaultAccountChangeEvent(final UUID id, final AccountModelDao oldData, final AccountModelDao newData, final Long searchKey1, final Long searchKey2, final UUID userToken) {
+    public DefaultAccountChangeEvent(final UUID id,
+                                     final AccountModelDao oldData,
+                                     final AccountModelDao newData,
+                                     final Long searchKey1,
+                                     final Long searchKey2,
+                                     final UUID userToken,
+                                     final DateTime changeDate) {
         super(searchKey1, searchKey2, userToken);
         this.accountId = id;
-        this.changedFields = calculateChangedFields(oldData, newData);
+        this.changedFields = calculateChangedFields(oldData, newData, changeDate);
     }
 
     @JsonIgnore
@@ -73,7 +83,7 @@ public class DefaultAccountChangeEvent extends BusEventBase implements AccountCh
     @JsonIgnore
     @Override
     public boolean hasChanges() {
-        return (changedFields.size() > 0);
+        return (!changedFields.isEmpty());
     }
 
     @Override
@@ -116,57 +126,56 @@ public class DefaultAccountChangeEvent extends BusEventBase implements AccountCh
         return true;
     }
 
-    private List<ChangedField> calculateChangedFields(final AccountModelDao oldData, final AccountModelDao newData) {
-
+    private List<ChangedField> calculateChangedFields(final AccountModelDao oldData, final AccountModelDao newData, final DateTime changeDate) {
         final List<ChangedField> tmpChangedFields = new ArrayList<ChangedField>();
 
         addIfValueChanged(tmpChangedFields, "externalKey",
-                          oldData.getExternalKey(), newData.getExternalKey());
+                          oldData.getExternalKey(), newData.getExternalKey(), changeDate);
 
         addIfValueChanged(tmpChangedFields, "email",
-                          oldData.getEmail(), newData.getEmail());
+                          oldData.getEmail(), newData.getEmail(), changeDate);
 
         addIfValueChanged(tmpChangedFields, "firstName",
-                          oldData.getName(), newData.getName());
+                          oldData.getName(), newData.getName(), changeDate);
 
         addIfValueChanged(tmpChangedFields, "currency",
                           (oldData.getCurrency() != null) ? oldData.getCurrency().toString() : null,
-                          (newData.getCurrency() != null) ? newData.getCurrency().toString() : null);
+                          (newData.getCurrency() != null) ? newData.getCurrency().toString() : null, changeDate);
 
         addIfValueChanged(tmpChangedFields,
                           "billCycleDayLocal",
-                          String.valueOf(oldData.getBillingCycleDayLocal()), String.valueOf(newData.getBillingCycleDayLocal()));
+                          String.valueOf(oldData.getBillingCycleDayLocal()), String.valueOf(newData.getBillingCycleDayLocal()), changeDate);
 
         addIfValueChanged(tmpChangedFields, "paymentMethodId",
                           (oldData.getPaymentMethodId() != null) ? oldData.getPaymentMethodId().toString() : null,
-                          (newData.getPaymentMethodId() != null) ? newData.getPaymentMethodId().toString() : null);
+                          (newData.getPaymentMethodId() != null) ? newData.getPaymentMethodId().toString() : null, changeDate);
 
-        addIfValueChanged(tmpChangedFields, "locale", oldData.getLocale(), newData.getLocale());
+        addIfValueChanged(tmpChangedFields, "locale", oldData.getLocale(), newData.getLocale(), changeDate);
 
         addIfValueChanged(tmpChangedFields, "timeZone",
                           (oldData.getTimeZone() == null) ? null : oldData.getTimeZone().toString(),
-                          (newData.getTimeZone() == null) ? null : newData.getTimeZone().toString());
+                          (newData.getTimeZone() == null) ? null : newData.getTimeZone().toString(), changeDate);
 
-        addIfValueChanged(tmpChangedFields, "address1", oldData.getAddress1(), newData.getAddress1());
-        addIfValueChanged(tmpChangedFields, "address2", oldData.getAddress2(), newData.getAddress2());
-        addIfValueChanged(tmpChangedFields, "city", oldData.getCity(), newData.getCity());
-        addIfValueChanged(tmpChangedFields, "stateOrProvince", oldData.getStateOrProvince(), newData.getStateOrProvince());
-        addIfValueChanged(tmpChangedFields, "country", oldData.getCountry(), newData.getCountry());
-        addIfValueChanged(tmpChangedFields, "postalCode", oldData.getPostalCode(), newData.getPostalCode());
-        addIfValueChanged(tmpChangedFields, "phone", oldData.getPhone(), newData.getPhone());
+        addIfValueChanged(tmpChangedFields, "address1", oldData.getAddress1(), newData.getAddress1(), changeDate);
+        addIfValueChanged(tmpChangedFields, "address2", oldData.getAddress2(), newData.getAddress2(), changeDate);
+        addIfValueChanged(tmpChangedFields, "city", oldData.getCity(), newData.getCity(), changeDate);
+        addIfValueChanged(tmpChangedFields, "stateOrProvince", oldData.getStateOrProvince(), newData.getStateOrProvince(), changeDate);
+        addIfValueChanged(tmpChangedFields, "country", oldData.getCountry(), newData.getCountry(), changeDate);
+        addIfValueChanged(tmpChangedFields, "postalCode", oldData.getPostalCode(), newData.getPostalCode(), changeDate);
+        addIfValueChanged(tmpChangedFields, "phone", oldData.getPhone(), newData.getPhone(), changeDate);
 
         return tmpChangedFields;
     }
 
-    private void addIfValueChanged(final List<ChangedField> inputList, final String key, final String oldData, final String newData) {
+    private void addIfValueChanged(final Collection<ChangedField> inputList, final String key, final String oldData, final String newData, final DateTime changeDate) {
         // If both null => no changes
         if (newData == null && oldData == null) {
             // If only one is null
         } else if (newData == null || oldData == null) {
-            inputList.add(new DefaultChangedField(key, oldData, newData));
+            inputList.add(new DefaultChangedField(key, oldData, newData, changeDate));
             // If neither are null we can safely compare values
         } else if (!newData.equals(oldData)) {
-            inputList.add(new DefaultChangedField(key, oldData, newData));
+            inputList.add(new DefaultChangedField(key, oldData, newData, changeDate));
         }
     }
 }
diff --git a/account/src/main/java/org/killbill/billing/account/dao/DefaultAccountDao.java b/account/src/main/java/org/killbill/billing/account/dao/DefaultAccountDao.java
index 31c33e3..a106fb0 100644
--- a/account/src/main/java/org/killbill/billing/account/dao/DefaultAccountDao.java
+++ b/account/src/main/java/org/killbill/billing/account/dao/DefaultAccountDao.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:
  *
@@ -57,6 +59,7 @@ public class DefaultAccountDao extends EntityDaoBase<AccountModelDao, Account, A
 
     private final PersistentBus eventBus;
     private final InternalCallContextFactory internalCallContextFactory;
+    private final Clock clock;
 
     @Inject
     public DefaultAccountDao(final IDBI dbi, final PersistentBus eventBus, final Clock clock, final CacheControllerDispatcher cacheControllerDispatcher,
@@ -64,6 +67,7 @@ public class DefaultAccountDao extends EntityDaoBase<AccountModelDao, Account, A
         super(new EntitySqlDaoTransactionalJdbiWrapper(dbi, clock, cacheControllerDispatcher, nonEntityDao), AccountSqlDao.class);
         this.eventBus = eventBus;
         this.internalCallContextFactory = internalCallContextFactory;
+        this.clock = clock;
     }
 
     @Override
@@ -157,8 +161,8 @@ public class DefaultAccountDao extends EntityDaoBase<AccountModelDao, Account, A
                                                                                              specifiedAccount,
                                                                                              context.getAccountRecordId(),
                                                                                              context.getTenantRecordId(),
-                                                                                             context.getUserToken()
-                );
+                                                                                             context.getUserToken(),
+                                                                                             clock.getUTCNow());
                 try {
                     eventBus.postFromTransaction(changeEvent, entitySqlDaoWrapperFactory.getHandle().getConnection());
                 } catch (final EventBusException e) {
@@ -195,8 +199,8 @@ public class DefaultAccountDao extends EntityDaoBase<AccountModelDao, Account, A
                 final AccountChangeInternalEvent changeEvent = new DefaultAccountChangeEvent(accountId, currentAccount, account,
                                                                                              context.getAccountRecordId(),
                                                                                              context.getTenantRecordId(),
-                                                                                             context.getUserToken()
-                );
+                                                                                             context.getUserToken(),
+                                                                                             clock.getUTCNow());
 
                 try {
                     eventBus.postFromTransaction(changeEvent, entitySqlDaoWrapperFactory.getHandle().getConnection());
diff --git a/account/src/test/java/org/killbill/billing/account/api/user/TestDefaultAccountUserApiWithMocks.java b/account/src/test/java/org/killbill/billing/account/api/user/TestDefaultAccountUserApiWithMocks.java
index f3d7863..6cf12f7 100644
--- a/account/src/test/java/org/killbill/billing/account/api/user/TestDefaultAccountUserApiWithMocks.java
+++ b/account/src/test/java/org/killbill/billing/account/api/user/TestDefaultAccountUserApiWithMocks.java
@@ -1,7 +1,7 @@
 /*
  * Copyright 2010-2013 Ning, Inc.
- * 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
@@ -52,7 +52,7 @@ public class TestDefaultAccountUserApiWithMocks extends AccountTestSuiteNoDB {
 
     @BeforeMethod(groups = "fast")
     public void setUp() throws Exception {
-        accountDao = new MockAccountDao(Mockito.mock(PersistentBus.class));
+        accountDao = new MockAccountDao(Mockito.mock(PersistentBus.class), clock);
         accountUserApi = new DefaultAccountUserApi(accountDao, nonEntityDao, controllerDispatcher, internalFactory);
     }
 
diff --git a/account/src/test/java/org/killbill/billing/account/api/user/TestEventJson.java b/account/src/test/java/org/killbill/billing/account/api/user/TestEventJson.java
index 23ccdf7..23e3aa3 100644
--- a/account/src/test/java/org/killbill/billing/account/api/user/TestEventJson.java
+++ b/account/src/test/java/org/killbill/billing/account/api/user/TestEventJson.java
@@ -37,8 +37,8 @@ public class TestEventJson extends AccountTestSuiteNoDB {
     @Test(groups = "fast", description="Test Account event deserialization")
     public void testDefaultAccountChangeEvent() throws Exception {
         final List<ChangedField> changes = new ArrayList<ChangedField>();
-        changes.add(new DefaultChangedField("fieldXX", "valueX", "valueXXX"));
-        changes.add(new DefaultChangedField("fieldYY", "valueY", "valueYYY"));
+        changes.add(new DefaultChangedField("fieldXX", "valueX", "valueXXX", clock.getUTCNow()));
+        changes.add(new DefaultChangedField("fieldYY", "valueY", "valueYYY", clock.getUTCNow()));
         final AccountChangeInternalEvent e = new DefaultAccountChangeEvent(changes, UUID.randomUUID(), 1L, 2L, null);
 
         final String json = mapper.writeValueAsString(e);
diff --git a/account/src/test/java/org/killbill/billing/account/dao/MockAccountDao.java b/account/src/test/java/org/killbill/billing/account/dao/MockAccountDao.java
index a06aa3f..0c33683 100644
--- a/account/src/test/java/org/killbill/billing/account/dao/MockAccountDao.java
+++ b/account/src/test/java/org/killbill/billing/account/dao/MockAccountDao.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,13 +18,12 @@
 
 package org.killbill.billing.account.dao;
 
+import java.util.Collection;
 import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
 import java.util.UUID;
 
-import org.testng.Assert;
-
 import org.killbill.billing.BillingExceptionBase;
 import org.killbill.billing.ErrorCode;
 import org.killbill.billing.account.api.Account;
@@ -33,8 +34,6 @@ import org.killbill.billing.account.api.DefaultMutableAccountData;
 import org.killbill.billing.account.api.user.DefaultAccountChangeEvent;
 import org.killbill.billing.account.api.user.DefaultAccountCreationEvent;
 import org.killbill.billing.account.api.user.DefaultAccountCreationEvent.DefaultAccountData;
-import org.killbill.bus.api.PersistentBus;
-import org.killbill.bus.api.PersistentBus.EventBusException;
 import org.killbill.billing.callcontext.InternalCallContext;
 import org.killbill.billing.callcontext.InternalTenantContext;
 import org.killbill.billing.events.AccountChangeInternalEvent;
@@ -42,6 +41,10 @@ import org.killbill.billing.util.callcontext.InternalCallContextFactory;
 import org.killbill.billing.util.entity.DefaultPagination;
 import org.killbill.billing.util.entity.Pagination;
 import org.killbill.billing.util.entity.dao.MockEntityDaoBase;
+import org.killbill.bus.api.PersistentBus;
+import org.killbill.bus.api.PersistentBus.EventBusException;
+import org.killbill.clock.Clock;
+import org.testng.Assert;
 
 import com.google.common.base.Predicate;
 import com.google.common.collect.ImmutableList;
@@ -52,10 +55,12 @@ public class MockAccountDao extends MockEntityDaoBase<AccountModelDao, Account, 
 
     private final MockEntityDaoBase<AccountEmailModelDao, AccountEmail, AccountApiException> accountEmailSqlDao = new MockEntityDaoBase<AccountEmailModelDao, AccountEmail, AccountApiException>();
     private final PersistentBus eventBus;
+    private final Clock clock;
 
     @Inject
-    public MockAccountDao(final PersistentBus eventBus) {
+    public MockAccountDao(final PersistentBus eventBus, final Clock clock) {
         this.eventBus = eventBus;
+        this.clock = clock;
     }
 
     @Override
@@ -81,8 +86,8 @@ public class MockAccountDao extends MockEntityDaoBase<AccountModelDao, Account, 
         final long tenantRecordId = context == null ? InternalCallContextFactory.INTERNAL_TENANT_RECORD_ID
                                                     : context.getTenantRecordId();
         final AccountChangeInternalEvent changeEvent = new DefaultAccountChangeEvent(account.getId(), currentAccount, account,
-                                                                                     accountRecordId, tenantRecordId, UUID.randomUUID()
-        );
+                                                                                     accountRecordId, tenantRecordId, UUID.randomUUID(),
+                                                                                     clock.getUTCNow());
         if (changeEvent.hasChanges()) {
             try {
                 eventBus.post(changeEvent);
@@ -106,7 +111,7 @@ public class MockAccountDao extends MockEntityDaoBase<AccountModelDao, Account, 
 
     @Override
     public Pagination<AccountModelDao> searchAccounts(final String searchKey, final Long offset, final Long limit, final InternalTenantContext context) {
-        final List<AccountModelDao> results = new LinkedList<AccountModelDao>();
+        final Collection<AccountModelDao> results = new LinkedList<AccountModelDao>();
         int maxNbRecords = 0;
         for (final AccountModelDao account : getAll(context)) {
             maxNbRecords++;
@@ -145,7 +150,7 @@ public class MockAccountDao extends MockEntityDaoBase<AccountModelDao, Account, 
     public void addEmail(final AccountEmailModelDao email, final InternalCallContext context) {
         try {
             accountEmailSqlDao.create(email, context);
-        } catch (BillingExceptionBase billingExceptionBase) {
+        } catch (final BillingExceptionBase billingExceptionBase) {
             Assert.fail(billingExceptionBase.toString());
         }
     }
@@ -170,5 +175,4 @@ public class MockAccountDao extends MockEntityDaoBase<AccountModelDao, Account, 
         final AccountModelDao account = getById(accountId, context);
         return account != null ? account.getBillingCycleDayLocal() : 0;
     }
-
 }

api/pom.xml 2(+1 -1)

diff --git a/api/pom.xml b/api/pom.xml
index 3ef8182..ef10494 100644
--- a/api/pom.xml
+++ b/api/pom.xml
@@ -19,7 +19,7 @@
     <parent>
         <artifactId>killbill</artifactId>
         <groupId>org.kill-bill.billing</groupId>
-        <version>0.16.1-SNAPSHOT</version>
+        <version>0.16.2-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>killbill-internal-api</artifactId>
diff --git a/api/src/main/java/org/killbill/billing/callcontext/InternalCallContext.java b/api/src/main/java/org/killbill/billing/callcontext/InternalCallContext.java
index f1429a0..7dced4f 100644
--- a/api/src/main/java/org/killbill/billing/callcontext/InternalCallContext.java
+++ b/api/src/main/java/org/killbill/billing/callcontext/InternalCallContext.java
@@ -53,8 +53,8 @@ public class InternalCallContext extends InternalTenantContext {
         this.contextUserType = userType;
         this.reasonCode = reasonCode;
         this.comments = comment;
-        this.createdDate = new DateTime(createdDate, DateTimeZone.UTC);
-        this.updatedDate = updatedDate;
+        this.createdDate = toUTCDateTime(createdDate);
+        this.updatedDate = toUTCDateTime(updatedDate);
     }
 
     public InternalCallContext(final Long tenantRecordId, @Nullable final Long accountRecordId, final CallContext callContext) {
diff --git a/api/src/main/java/org/killbill/billing/callcontext/InternalTenantContext.java b/api/src/main/java/org/killbill/billing/callcontext/InternalTenantContext.java
index f35241d..8995fd1 100644
--- a/api/src/main/java/org/killbill/billing/callcontext/InternalTenantContext.java
+++ b/api/src/main/java/org/killbill/billing/callcontext/InternalTenantContext.java
@@ -25,7 +25,7 @@ import org.killbill.billing.util.callcontext.TenantContext;
 /**
  * Internal use only
  */
-public class InternalTenantContext {
+public class InternalTenantContext extends TimeAwareContext {
 
     protected final Long tenantRecordId;
     protected final Long accountRecordId;
diff --git a/api/src/main/java/org/killbill/billing/callcontext/TimeAwareContext.java b/api/src/main/java/org/killbill/billing/callcontext/TimeAwareContext.java
new file mode 100644
index 0000000..bb68757
--- /dev/null
+++ b/api/src/main/java/org/killbill/billing/callcontext/TimeAwareContext.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2016 Groupon, Inc
+ * Copyright 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
+ * License.  You may obtain a copy of the License at:
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.killbill.billing.callcontext;
+
+import java.util.Date;
+
+import org.joda.time.DateTime;
+import org.joda.time.DateTimeZone;
+import org.joda.time.LocalDate;
+
+public class TimeAwareContext {
+
+    // From JDK to Joda (see http://www.joda.org/joda-time/userguide.html#JDK_Interoperability)
+    public DateTime toUTCDateTime(final Date date) {
+        return toUTCDateTime(new DateTime(date));
+    }
+
+    // Create a DateTime object forcing the time zone to be UTC
+    public DateTime toUTCDateTime(final DateTime dateTime) {
+        return toDateTime(dateTime, DateTimeZone.UTC);
+    }
+
+    // Create a DateTime object using the specified timezone (usually, the one on the account)
+    // TODO Should we cache the accountTimeZone in the context?
+    public DateTime toDateTime(final DateTime dateTime, final DateTimeZone accountTimeZone) {
+        return dateTime.toDateTime(accountTimeZone);
+    }
+
+    // Create a LocalDate object using the specified timezone (usually, the one on the account)
+    // TODO Should we cache the accountTimeZone in the context?
+    public LocalDate toLocalDate(final DateTime dateTime, final DateTimeZone accountTimeZone) {
+        return new LocalDate(dateTime, accountTimeZone);
+    }
+}
diff --git a/api/src/main/java/org/killbill/billing/invoice/api/InvoiceInternalApi.java b/api/src/main/java/org/killbill/billing/invoice/api/InvoiceInternalApi.java
index b50879e..6c44797 100644
--- a/api/src/main/java/org/killbill/billing/invoice/api/InvoiceInternalApi.java
+++ b/api/src/main/java/org/killbill/billing/invoice/api/InvoiceInternalApi.java
@@ -43,8 +43,6 @@ public interface InvoiceInternalApi {
 
     public InvoicePayment getInvoicePaymentForAttempt(UUID paymentId, InternalTenantContext context) throws InvoiceApiException;
 
-    public InvoicePayment getInvoicePaymentForRefund(UUID paymentId, InternalTenantContext context) throws InvoiceApiException;
-
     public InvoicePayment getInvoicePaymentForChargeback(UUID paymentId, InternalTenantContext context) throws InvoiceApiException;
 
     public Invoice getInvoiceForPaymentId(UUID paymentId, InternalTenantContext context) throws InvoiceApiException;

beatrix/pom.xml 2(+1 -1)

diff --git a/beatrix/pom.xml b/beatrix/pom.xml
index 37aa74e..0376865 100644
--- a/beatrix/pom.xml
+++ b/beatrix/pom.xml
@@ -19,7 +19,7 @@
     <parent>
         <artifactId>killbill</artifactId>
         <groupId>org.kill-bill.billing</groupId>
-        <version>0.16.1-SNAPSHOT</version>
+        <version>0.16.2-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>killbill-beatrix</artifactId>
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 b97040b..2fe3884 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
@@ -943,9 +943,9 @@ public class TestOverdueIntegration extends TestOverdueBase {
                     createPaymentAndCheckForCompletion(account, invoice, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
                 } else {
                     if (extraPayment) {
-                        createPaymentAndCheckForCompletion(account, invoice, NextEvent.BLOCK, NextEvent.TAG, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
+                        createPaymentAndCheckForCompletion(account, invoice, NextEvent.BLOCK, NextEvent.TAG, NextEvent.NULL_INVOICE, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
                     } else {
-                        createPaymentAndCheckForCompletion(account, invoice, NextEvent.BLOCK, NextEvent.TAG, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
+                        createPaymentAndCheckForCompletion(account, invoice, NextEvent.BLOCK, NextEvent.TAG, NextEvent.NULL_INVOICE, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
                     }
                 }
             }
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 9575752..f3fa2f8 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
@@ -84,10 +84,10 @@ public class TestOverdueWithSubscriptionCancellation extends TestOverdueBase {
 
         // Cancel addOn1 one day after
         clock.addDays(1);
-        cancelEntitlementAndCheckForCompletion(addOn1, clock.getUTCNow(), NextEvent.BLOCK, NextEvent.CANCEL);
+        cancelEntitlementAndCheckForCompletion(addOn1, clock.getUTCNow(), NextEvent.BLOCK, NextEvent.CANCEL, NextEvent.NULL_INVOICE);
 
         // DAY 30 have to get out of trial before first payment
-        addDaysAndCheckForCompletion(29, NextEvent.PHASE,  NextEvent.PHASE, NextEvent.INVOICE, NextEvent.PAYMENT_ERROR, NextEvent.INVOICE_PAYMENT_ERROR);
+        addDaysAndCheckForCompletion(29, NextEvent.PHASE,  NextEvent.PHASE, NextEvent.NULL_INVOICE, NextEvent.INVOICE, NextEvent.PAYMENT_ERROR, NextEvent.INVOICE_PAYMENT_ERROR);
 
         invoiceChecker.checkChargedThroughDate(baseEntitlement.getId(), new LocalDate(2012, 6, 30), callContext);
 
@@ -135,12 +135,12 @@ public class TestOverdueWithSubscriptionCancellation extends TestOverdueBase {
 
         // Cancel bundle 2 one day after (2012-05-02)
         clock.addDays(1);
-        cancelEntitlementAndCheckForCompletion(baseEntitlement2, clock.getUTCNow(), NextEvent.BLOCK, NextEvent.CANCEL);
+        cancelEntitlementAndCheckForCompletion(baseEntitlement2, clock.getUTCNow(), NextEvent.BLOCK, NextEvent.CANCEL, NextEvent.NULL_INVOICE);
         final SubscriptionBase cancelledBaseSubscription2 = ((DefaultEntitlement) entitlementApi.getEntitlementForId(baseEntitlement2.getId(), callContext)).getSubscriptionBase();
         assertTrue(cancelledBaseSubscription2.getState() == EntitlementState.CANCELLED);
 
         // DAY 30 have to get out of trial before first payment (2012-05-31)
-        addDaysAndCheckForCompletion(29, NextEvent.PHASE,  NextEvent.PHASE, NextEvent.INVOICE, NextEvent.PAYMENT_ERROR, NextEvent.INVOICE_PAYMENT_ERROR);
+        addDaysAndCheckForCompletion(29, NextEvent.PHASE,  NextEvent.PHASE, NextEvent.NULL_INVOICE, NextEvent.INVOICE, NextEvent.PAYMENT_ERROR, NextEvent.INVOICE_PAYMENT_ERROR);
         invoiceChecker.checkInvoice(account.getId(),
                                     4,
                                     callContext,
diff --git a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestBundleTransfer.java b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestBundleTransfer.java
index 7915e6e..e32ab28 100644
--- a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestBundleTransfer.java
+++ b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestBundleTransfer.java
@@ -55,7 +55,6 @@ public class TestBundleTransfer extends TestIntegrationBase {
 
     @Test(groups = "slow")
     public void testBundleTransferWithBPAnnualOnly() throws Exception {
-
         final Account account = createAccountWithNonOsgiPaymentMethod(getAccountData(3));
 
         // Set clock to the initial start date - we implicitly assume here that the account timezone is UTC
@@ -88,23 +87,21 @@ public class TestBundleTransfer extends TestIntegrationBase {
         transferApi.transferBundle(account.getId(), newAccount.getId(), "externalKey", clock.getUTCNow(), false, false, callContext);
         assertListenerStatus();
 
-        List<Invoice> invoices = invoiceUserApi.getInvoicesByAccount(newAccount.getId(), callContext);
+        final List<Invoice> invoices = invoiceUserApi.getInvoicesByAccount(newAccount.getId(), callContext);
         assertEquals(invoices.size(), 1);
 
         final List<InvoiceItem> invoiceItems = invoices.get(0).getInvoiceItems();
         assertEquals(invoiceItems.size(), 1);
-        InvoiceItem theItem = invoiceItems.get(0);
+        final InvoiceItem theItem = invoiceItems.get(0);
         assertTrue(theItem.getStartDate().compareTo(new LocalDate(2012, 5, 11)) == 0);
         assertTrue(theItem.getEndDate().compareTo(new LocalDate(2013, 5, 11)) == 0);
         assertTrue(theItem.getAmount().compareTo(new BigDecimal("2399.9500")) == 0);
 
         checkNoMoreInvoiceToGenerate(account);
-
     }
 
     @Test(groups = "slow")
     public void testBundleTransferWithBPMonthlyOnly() throws Exception {
-
         final Account account = createAccountWithNonOsgiPaymentMethod(getAccountData(0));
 
         // Set clock to the initial start date - we implicitly assume here that the account timezone is UTC
@@ -249,7 +246,7 @@ public class TestBundleTransfer extends TestIntegrationBase {
         paymentChecker.checkPayment(account.getId(), 1, callContext, new ExpectedPaymentCheck(new LocalDate(2012, 4, 1), new BigDecimal("399.95"), TransactionStatus.SUCCESS, secondInvoice.getId(), Currency.USD));
 
         // Move past the phase for simplicity
-        busHandler.pushExpectedEvents(NextEvent.PHASE, NextEvent.PHASE, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
+        busHandler.pushExpectedEvents(NextEvent.PHASE, NextEvent.PHASE, NextEvent.NULL_INVOICE, NextEvent.NULL_INVOICE, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
         clock.addDays(30);
         assertListenerStatus();
         final Invoice thirdInvoice = invoiceChecker.checkInvoice(account.getId(), 3, callContext,
@@ -270,7 +267,7 @@ public class TestBundleTransfer extends TestIntegrationBase {
         final DateTime now = clock.getUTCNow();
         final LocalDate transferDay = now.toLocalDate();
 
-        busHandler.pushExpectedEvents(NextEvent.CANCEL, NextEvent.CANCEL, NextEvent.BLOCK, NextEvent.BLOCK, NextEvent.TRANSFER, NextEvent.TRANSFER, NextEvent.INVOICE, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
+        busHandler.pushExpectedEvents(NextEvent.CANCEL, NextEvent.CANCEL, NextEvent.BLOCK, NextEvent.BLOCK, NextEvent.TRANSFER, NextEvent.TRANSFER, NextEvent.NULL_INVOICE, NextEvent.NULL_INVOICE, NextEvent.INVOICE, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
         final UUID newBundleId = entitlementApi.transferEntitlements(account.getId(), newAccount.getId(), bundleExternalKey, transferDay, ImmutableList.<PluginProperty>of(), callContext);
         assertListenerStatus();
 
@@ -313,6 +310,5 @@ public class TestBundleTransfer extends TestIntegrationBase {
         }
 
         checkNoMoreInvoiceToGenerate(account);
-
     }
 }
diff --git a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestCatalogRetireElements.java b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestCatalogRetireElements.java
index 3a8a532..5c0e974 100644
--- a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestCatalogRetireElements.java
+++ b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestCatalogRetireElements.java
@@ -50,7 +50,6 @@ public class TestCatalogRetireElements extends TestIntegrationBase {
 
     @Test(groups = "slow")
     public void testRetirePlan() throws Exception {
-
         // Catalog v1 starts in 2011-01-01
         // Catalog v2 starts in 2015-12-01
         final LocalDate today = new LocalDate(2015, 11, 5);
@@ -85,7 +84,7 @@ public class TestCatalogRetireElements extends TestIntegrationBase {
         try {
             entitlementApi.createBaseEntitlement(account.getId(), spec, "externalKey2", null, effectiveDate, ImmutableList.<PluginProperty>of(), callContext);
             fail(); // force to fail is there is not an exception
-        } catch (EntitlementApiException e) {
+        } catch (final EntitlementApiException e) {
             assertTrue(e.getLocalizedMessage().startsWith("Could not find a plan matching: (product: 'Pistol', billing period: 'MONTHLY'"));
         }
 
@@ -96,15 +95,13 @@ public class TestCatalogRetireElements extends TestIntegrationBase {
 
         final List<Invoice> invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), callContext);
         assertEquals(invoices.size(), 3);
-        for (Invoice invoice : invoices) {
+        for (final Invoice invoice : invoices) {
             assertEquals(invoice.getInvoiceItems().get(0).getPlanName(), "pistol-monthly");
         }
-
     }
 
     @Test(groups = "slow")
     public void testRetireProduct() throws Exception {
-
         // Catalog v1 starts in 2011-01-01
         // Catalog v3 starts in 2016-01-01
         final LocalDate today = new LocalDate(2015, 11, 5);
@@ -144,7 +141,7 @@ public class TestCatalogRetireElements extends TestIntegrationBase {
         try {
             entitlementApi.createBaseEntitlement(account.getId(), spec, "externalKey2", null, effectiveDate, ImmutableList.<PluginProperty>of(), callContext);
             fail(); // force to fail is there is not an exception
-        } catch (EntitlementApiException e) {
+        } catch (final EntitlementApiException e) {
             assertTrue(e.getLocalizedMessage().startsWith("Could not find any product named 'Pistol'"));
         }
 
@@ -155,15 +152,13 @@ public class TestCatalogRetireElements extends TestIntegrationBase {
 
         final List<Invoice> invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), callContext);
         assertEquals(invoices.size(), 4);
-        for (Invoice invoice : invoices) {
+        for (final Invoice invoice : invoices) {
             assertEquals(invoice.getInvoiceItems().get(0).getPlanName(), "pistol-monthly");
         }
-
     }
 
     @Test(groups = "slow")
     public void testRetirePriceList() throws Exception {
-
         // Catalog v1 starts in 2011-01-01
         // Catalog v2 starts in 2015-12-01
         // Catalog v3 starts in 2016-01-01
@@ -203,7 +198,7 @@ public class TestCatalogRetireElements extends TestIntegrationBase {
         try {
             entitlementApi.createBaseEntitlement(account.getId(), spec, "externalKey2", null, effectiveDate, ImmutableList.<PluginProperty>of(), callContext);
             fail(); // force to fail is there is not an exception
-        } catch (EntitlementApiException e) {
+        } catch (final EntitlementApiException e) {
             assertTrue(e.getLocalizedMessage().startsWith("Could not find any product named 'Pistol'"));
         }
 
@@ -213,7 +208,7 @@ public class TestCatalogRetireElements extends TestIntegrationBase {
         assertListenerStatus();
 
         // Move out a month.
-        busHandler.pushExpectedEvents(NextEvent.PHASE, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
+        busHandler.pushExpectedEvents(NextEvent.PHASE, NextEvent.NULL_INVOICE, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
         clock.addMonths(1);
         assertListenerStatus();
 
@@ -225,7 +220,5 @@ public class TestCatalogRetireElements extends TestIntegrationBase {
         assertTrue(invoices.get(2).getInvoiceItems().get(0).getPhaseName().equals("discount-pistol-monthly-discount"));
         assertTrue(invoices.get(3).getInvoiceItems().get(0).getPhaseName().equals("discount-pistol-monthly-discount"));
         assertTrue(invoices.get(4).getInvoiceItems().get(0).getPhaseName().equals("discount-pistol-monthly-evergreen"));
-
     }
-
 }
diff --git a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestIntegration.java b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestIntegration.java
index b1ff283..27642ab 100644
--- a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestIntegration.java
+++ b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestIntegration.java
@@ -27,6 +27,7 @@ import org.joda.time.DateTime;
 import org.joda.time.DateTimeZone;
 import org.joda.time.Interval;
 import org.joda.time.LocalDate;
+import org.killbill.billing.ErrorCode;
 import org.killbill.billing.account.api.Account;
 import org.killbill.billing.account.api.AccountData;
 import org.killbill.billing.api.TestApiListener.NextEvent;
@@ -65,7 +66,6 @@ public class TestIntegration extends TestIntegrationBase {
 
     @Test(groups = "slow")
     public void testCancelBPWithAOTheSameDay() throws Exception {
-
         final AccountData accountData = getAccountData(1);
         final Account account = createAccountWithNonOsgiPaymentMethod(accountData);
         accountChecker.checkAccount(account.getId(), accountData, callContext);
@@ -103,7 +103,7 @@ public class TestIntegration extends TestIntegrationBase {
 
         addAOEntitlementAndCheckForCompletion(bpSubscription.getBundleId(), "Telescopic-Scope", ProductCategory.ADD_ON, BillingPeriod.MONTHLY, NextEvent.CREATE, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
 
-        Invoice invoice = invoiceChecker.checkInvoice(account.getId(), 2, callContext, expectedInvoices);
+        final Invoice invoice = invoiceChecker.checkInvoice(account.getId(), 2, callContext, expectedInvoices);
         paymentChecker.checkPayment(account.getId(), 1, callContext, new ExpectedPaymentCheck(new LocalDate(2012, 4, 1), new BigDecimal("399.95"), TransactionStatus.SUCCESS, invoice.getId(), Currency.USD));
         expectedInvoices.clear();
 
@@ -128,16 +128,13 @@ public class TestIntegration extends TestIntegrationBase {
                                     expectedInvoices);
 
         checkNoMoreInvoiceToGenerate(account);
-
     }
 
     @Test(groups = "slow")
     public void testBasePlanCompleteWithBillingDayInPast() throws Exception {
-
         final int billingDay = 31;
         final DateTime initialCreationDate = new DateTime(2012, 2, 1, 0, 3, 42, 0, testTimeZone);
 
-        log.info("Beginning test with BCD of " + billingDay);
         final Account account = createAccountWithNonOsgiPaymentMethod(getAccountData(billingDay));
 
         // set clock to the initial start date
@@ -213,7 +210,7 @@ public class TestIntegration extends TestIntegrationBase {
 
         invoiceChecker.checkInvoiceNoAudits(dryRunInvoice, callContext, expectedInvoices);
 
-        addDaysAndCheckForCompletion(31, NextEvent.CHANGE, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
+        addDaysAndCheckForCompletion(31, NextEvent.CHANGE, NextEvent.NULL_INVOICE, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
         invoiceChecker.checkInvoice(account.getId(), invoiceItemCount++, callContext, expectedInvoices);
         invoiceChecker.checkChargedThroughDate(subscription.getId(), secondRecurringPistolDate, callContext);
         expectedInvoices.clear();
@@ -239,21 +236,17 @@ public class TestIntegration extends TestIntegrationBase {
         baseEntitlement = cancelEntitlementAndCheckForCompletion(baseEntitlement, clock.getUTCNow(), NextEvent.BLOCK);
 
         // MOVE AFTER CANCEL DATE AND EXPECT EVENT : NextEvent.CANCEL
-        addDaysAndCheckForCompletion(31, NextEvent.CANCEL);
+        addDaysAndCheckForCompletion(31, NextEvent.CANCEL, NextEvent.NULL_INVOICE, NextEvent.NULL_INVOICE);
         invoiceChecker.checkChargedThroughDate(subscription.getId(), new LocalDate(2012, 7, 31), callContext);
 
         checkNoMoreInvoiceToGenerate(account);
-
-        log.info("TEST PASSED !");
     }
 
     @Test(groups = "slow")
     public void testBasePlanCompleteWithBillingDayAlignedWithTrial() throws Exception {
-
         final int billingDay = 2;
         final DateTime initialCreationDate = new DateTime(2012, 2, 1, 0, 3, 42, 0, testTimeZone);
 
-        log.info("Beginning test with BCD of " + billingDay);
         final Account account = createAccountWithNonOsgiPaymentMethod(getAccountData(billingDay));
 
         // set clock to the initial start date
@@ -292,17 +285,15 @@ public class TestIntegration extends TestIntegrationBase {
         //
 
 
-        TestDryRunArguments dryRun = new TestDryRunArguments(DryRunType.SUBSCRIPTION_ACTION, "Pistol", ProductCategory.BASE, BillingPeriod.MONTHLY, null, null, SubscriptionEventType.CHANGE,
-                                                             subscription.getId(), subscription.getBundleId(), null, null);
+        final TestDryRunArguments dryRun = new TestDryRunArguments(DryRunType.SUBSCRIPTION_ACTION, "Pistol", ProductCategory.BASE, BillingPeriod.MONTHLY, null, null, SubscriptionEventType.CHANGE,
+                                                                   subscription.getId(), subscription.getBundleId(), null, null);
         try {
            invoiceUserApi.triggerInvoiceGeneration(account.getId(), clock.getUTCToday(), dryRun, callContext);
             Assert.fail("Call should return no invoices");
-        } catch (InvoiceApiException e) {
-            System.out.print("foo");
-
+        } catch (final InvoiceApiException e) {
+            assertEquals(e.getCode(), ErrorCode.INVOICE_NOTHING_TO_DO.getCode());
         }
 
-
         baseEntitlement = changeEntitlementAndCheckForCompletion(baseEntitlement, "Pistol", BillingPeriod.MONTHLY, null);
         subscription = subscriptionDataFromSubscription(baseEntitlement.getSubscriptionBase());
 
@@ -311,7 +302,7 @@ public class TestIntegration extends TestIntegrationBase {
         //
         final LocalDate firstRecurringPistolDate = subscription.getChargedThroughDate().toLocalDate();
         final LocalDate secondRecurringPistolDate = firstRecurringPistolDate.plusMonths(1);
-        addDaysAndCheckForCompletion(31, NextEvent.CHANGE, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
+        addDaysAndCheckForCompletion(31, NextEvent.CHANGE, NextEvent.NULL_INVOICE, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
         invoiceChecker.checkInvoice(account.getId(), invoiceItemCount++, callContext, new ExpectedInvoiceItemCheck(new LocalDate(2012, 4, 2), new LocalDate(2012, 5, 2), InvoiceItemType.RECURRING, new BigDecimal("29.95")));
         invoiceChecker.checkChargedThroughDate(subscription.getId(), secondRecurringPistolDate, callContext);
 
@@ -335,21 +326,18 @@ public class TestIntegration extends TestIntegrationBase {
         //
         cancelEntitlementAndCheckForCompletion(baseEntitlement, clock.getUTCNow(), NextEvent.BLOCK);
 
-        // MOVE AFTER CANCEL DATE AND EXPECT EVENT : NextEvent.CANCEL
-        addDaysAndCheckForCompletion(31, NextEvent.CANCEL);
+        // MOVE AFTER CANCEL DATE
+        addDaysAndCheckForCompletion(31, NextEvent.CANCEL, NextEvent.NULL_INVOICE, NextEvent.NULL_INVOICE);
         invoiceChecker.checkChargedThroughDate(subscription.getId(), new LocalDate(2012, 8, 2), callContext);
 
         checkNoMoreInvoiceToGenerate(account);
-
     }
 
     @Test(groups = "slow")
     public void testBasePlanCompleteWithBillingDayInFuture() throws Exception {
-
         final int billingDay = 3;
         final DateTime initialCreationDate = new DateTime(2012, 2, 1, 0, 3, 42, 0, testTimeZone);
 
-        log.info("Beginning test with BCD of " + billingDay);
         final Account account = createAccountWithNonOsgiPaymentMethod(getAccountData(billingDay));
 
         // set clock to the initial start date
@@ -401,7 +389,7 @@ public class TestIntegration extends TestIntegrationBase {
         //
         final LocalDate firstRecurringPistolDate = subscription.getChargedThroughDate().toLocalDate();
         final LocalDate secondRecurringPistolDate = firstRecurringPistolDate.plusMonths(1);
-        addDaysAndCheckForCompletion(31, NextEvent.CHANGE, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
+        addDaysAndCheckForCompletion(31, NextEvent.CHANGE, NextEvent.NULL_INVOICE, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
         invoiceChecker.checkInvoice(account.getId(), invoiceItemCount++, callContext, new ExpectedInvoiceItemCheck(new LocalDate(2012, 4, 3), new LocalDate(2012, 5, 3), InvoiceItemType.RECURRING, new BigDecimal("29.95")));
         invoiceChecker.checkChargedThroughDate(subscription.getId(), secondRecurringPistolDate, callContext);
 
@@ -426,13 +414,10 @@ public class TestIntegration extends TestIntegrationBase {
         baseEntitlement = cancelEntitlementAndCheckForCompletion(baseEntitlement, clock.getUTCNow(), NextEvent.BLOCK);
 
         // MOVE AFTER CANCEL DATE AND EXPECT EVENT : NextEvent.CANCEL
-        addDaysAndCheckForCompletion(31, NextEvent.CANCEL);
+        addDaysAndCheckForCompletion(31, NextEvent.CANCEL, NextEvent.NULL_INVOICE, NextEvent.NULL_INVOICE);
         invoiceChecker.checkChargedThroughDate(subscription.getId(), new LocalDate(2012, 8, 3), callContext);
 
         checkNoMoreInvoiceToGenerate(account);
-
-        log.info("TEST PASSED !");
-
     }
 
     @Test(groups = {"stress"}, enabled = false)
@@ -494,7 +479,6 @@ public class TestIntegration extends TestIntegrationBase {
 
         // MOVE CLOCK A LITTLE BIT-- STILL IN TRIAL
         final Interval it = new Interval(clock.getUTCNow(), clock.getUTCNow().plusDays(3));
-        log.info("Moving clock from" + clock.getUTCNow() + " to " + clock.getUTCNow().plusDays(3));
         clock.addDays(3);
 
         final DefaultEntitlement aoEntitlement1 = addAOEntitlementAndCheckForCompletion(baseEntitlement.getBundleId(), "Telescopic-Scope", ProductCategory.ADD_ON, BillingPeriod.MONTHLY,
@@ -504,36 +488,29 @@ public class TestIntegration extends TestIntegrationBase {
                                                                                         NextEvent.CREATE, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
 
         // MOVE CLOCK A LITTLE BIT MORE -- EITHER STAY IN TRIAL OR GET OUT
-        busHandler.pushExpectedEvents(NextEvent.PHASE, NextEvent.PHASE, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
-        log.info("Moving clock from" + clock.getUTCNow() + " to " + clock.getUTCNow().plusDays(28));
+        busHandler.pushExpectedEvents(NextEvent.PHASE, NextEvent.PHASE, NextEvent.NULL_INVOICE, NextEvent.NULL_INVOICE, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
         clock.addDays(28);// 26 / 5
         assertListenerStatus();
 
-        busHandler.pushExpectedEvents(NextEvent.PHASE, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
-        log.info("Moving clock from" + clock.getUTCNow() + " to " + clock.getUTCNow().plusDays(3));
+        busHandler.pushExpectedEvents(NextEvent.PHASE, NextEvent.NULL_INVOICE, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
         clock.addDays(3);// 29 / 5
         assertListenerStatus();
 
-        log.info("Moving clock from" + clock.getUTCNow() + " to " + clock.getUTCNow().plusDays(10));
         clock.addDays(10);// 8 / 6
         assertListenerStatus();
 
         busHandler.pushExpectedEvents(NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
-        log.info("Moving clock from" + clock.getUTCNow() + " to " + clock.getUTCNow().plusDays(18));
         clock.addDays(18);// 26 / 6
         assertListenerStatus();
 
-        log.info("Moving clock from" + clock.getUTCNow() + " to " + clock.getUTCNow().plusDays(3));
         clock.addDays(3);
         assertListenerStatus();
 
         checkNoMoreInvoiceToGenerate(account);
-
     }
 
     @Test(groups = "slow")
     public void testCreateMultipleBPWithSameExternalKey() throws Exception {
-
         final DateTime initialDate = new DateTime(2012, 4, 25, 0, 13, 42, 0, testTimeZone);
         clock.setDeltaFromReality(initialDate.getMillis() - clock.getUTCNow().getMillis());
 
@@ -546,7 +523,7 @@ public class TestIntegration extends TestIntegrationBase {
         final DefaultEntitlement baseEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "bundleKey", productName, ProductCategory.BASE, term, NextEvent.CREATE, NextEvent.INVOICE);
         final SubscriptionBundle initialBundle = subscriptionApi.getActiveSubscriptionBundleForExternalKey("bundleKey", callContext);
 
-        busHandler.pushExpectedEvents(NextEvent.BLOCK, NextEvent.CANCEL);
+        busHandler.pushExpectedEvents(NextEvent.BLOCK, NextEvent.CANCEL, NextEvent.NULL_INVOICE);
         baseEntitlement.cancelEntitlementWithPolicy(EntitlementActionPolicy.IMMEDIATE, ImmutableList.<PluginProperty>of(), callContext);
         assertListenerStatus();
 
@@ -565,7 +542,6 @@ public class TestIntegration extends TestIntegrationBase {
         assertEquals(newBaseEntitlement.getState(), EntitlementState.ACTIVE);
 
         checkNoMoreInvoiceToGenerate(account);
-
     }
 
     @Test(groups = "slow")
@@ -573,7 +549,6 @@ public class TestIntegration extends TestIntegrationBase {
         final DateTime initialDate = new DateTime(2012, 2, 1, 0, 3, 42, 0, testTimeZone);
         final int billingDay = 2;
 
-        log.info("Beginning test with BCD of " + billingDay);
         final Account account = createAccountWithNonOsgiPaymentMethod(getAccountData(billingDay));
         final UUID accountId = account.getId();
         assertNotNull(account);
@@ -581,9 +556,9 @@ public class TestIntegration extends TestIntegrationBase {
         // set clock to the initial start date
         clock.setDeltaFromReality(initialDate.getMillis() - clock.getUTCNow().getMillis());
 
-        String productName = "Shotgun";
-        BillingPeriod term = BillingPeriod.MONTHLY;
-        String planSetName = PriceListSet.DEFAULT_PRICELIST_NAME;
+        final String productName = "Shotgun";
+        final BillingPeriod term = BillingPeriod.MONTHLY;
+        final String planSetName = PriceListSet.DEFAULT_PRICELIST_NAME;
 
         //
         // CREATE SUBSCRIPTION AND EXPECT BOTH EVENTS: NextEvent.CREATE NextEvent.INVOICE
@@ -594,7 +569,7 @@ public class TestIntegration extends TestIntegrationBase {
         //
         // VERIFY CTD HAS BEEN SET
         //
-        DefaultSubscriptionBase subscription = (DefaultSubscriptionBase) baseEntitlement.getSubscriptionBase();
+        final DefaultSubscriptionBase subscription = (DefaultSubscriptionBase) baseEntitlement.getSubscriptionBase();
         final DateTime startDate = subscription.getCurrentPhaseStart();
         final BigDecimal rate = subscription.getCurrentPhase().getFixed().getPrice().getPrice(Currency.USD);
         verifyTestResult(accountId, subscription.getId(), startDate, null, rate, clock.getUTCNow(), 1);
@@ -610,8 +585,7 @@ public class TestIntegration extends TestIntegrationBase {
 
         // PAUSE THE ENTITLEMENT
         DefaultEntitlement entitlement = (DefaultEntitlement) entitlementApi.getEntitlementForId(baseEntitlement.getId(), callContext);
-        busHandler.pushExpectedEvents(NextEvent.PAUSE, NextEvent.BLOCK);
-        busHandler.pushExpectedEvent(NextEvent.INVOICE);
+        busHandler.pushExpectedEvents(NextEvent.PAUSE, NextEvent.BLOCK, NextEvent.NULL_INVOICE, NextEvent.INVOICE);
         entitlementApi.pause(entitlement.getBundleId(), clock.getUTCNow().toLocalDate(), ImmutableList.<PluginProperty>of(), callContext);
         assertListenerStatus();
 
@@ -628,8 +602,7 @@ public class TestIntegration extends TestIntegrationBase {
         // MOVE CLOCK FORWARD ADN CHECK THERE IS NO NEW INVOICE
         clock.addDeltaFromReality(AT_LEAST_ONE_MONTH_MS);
 
-        busHandler.pushExpectedEvents(NextEvent.RESUME, NextEvent.BLOCK);
-        busHandler.pushExpectedEvent(NextEvent.INVOICE);
+        busHandler.pushExpectedEvents(NextEvent.RESUME, NextEvent.BLOCK, NextEvent.NULL_INVOICE, NextEvent.NULL_INVOICE, NextEvent.INVOICE);
         entitlementApi.resume(entitlement.getBundleId(), clock.getUTCNow().toLocalDate(), ImmutableList.<PluginProperty>of(), callContext);
         assertListenerStatus();
 
@@ -638,14 +611,10 @@ public class TestIntegration extends TestIntegrationBase {
                                     new ExpectedInvoiceItemCheck(new LocalDate(2012, 4, 5), new LocalDate(2012, 4, 5), InvoiceItemType.CBA_ADJ, new BigDecimal("-224.96")));
 
         checkNoMoreInvoiceToGenerate(account);
-
     }
 
     @Test(groups = "slow")
     public void testForMultipleRecurringPhases() throws Exception {
-
-        log.info("Starting testForMultipleRecurringPhases");
-
         final DateTime initialCreationDate = new DateTime(2012, 2, 1, 0, 3, 42, 0, testTimeZone);
         clock.setDeltaFromReality(initialCreationDate.getMillis() - clock.getUTCNow().getMillis());
 
@@ -675,7 +644,7 @@ public class TestIntegration extends TestIntegrationBase {
             assertListenerStatus();
         }
 
-        busHandler.pushExpectedEvents(NextEvent.PHASE, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
+        busHandler.pushExpectedEvents(NextEvent.PHASE, NextEvent.NULL_INVOICE, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
         clock.addDeltaFromReality(AT_LEAST_ONE_MONTH_MS);
         assertListenerStatus();
 
diff --git a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestIntegrationBase.java b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestIntegrationBase.java
index cf2dae1..fef40bc 100644
--- a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestIntegrationBase.java
+++ b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestIntegrationBase.java
@@ -20,6 +20,7 @@ package org.killbill.billing.beatrix.integration;
 
 import java.math.BigDecimal;
 import java.util.ArrayList;
+import java.util.Collection;
 import java.util.List;
 import java.util.Map;
 import java.util.UUID;
@@ -308,10 +309,12 @@ public class TestIntegrationBase extends BeatrixTestSuiteWithEmbeddedDB {
     }
 
     protected void checkNoMoreInvoiceToGenerate(final Account account) {
+        busHandler.pushExpectedEvent(NextEvent.NULL_INVOICE);
         try {
             invoiceUserApi.triggerInvoiceGeneration(account.getId(), clock.getUTCToday(), null, callContext);
             fail("Should not have generated an extra invoice");
         } catch (final InvoiceApiException e) {
+            assertListenerStatus();
             assertEquals(e.getCode(), ErrorCode.INVOICE_NOTHING_TO_DO.getCode());
         }
     }
@@ -467,11 +470,15 @@ public class TestIntegrationBase extends BeatrixTestSuiteWithEmbeddedDB {
     }
 
     protected Payment refundPaymentAndCheckForCompletion(final Account account, final Payment payment, final NextEvent... events) {
+        return refundPaymentAndCheckForCompletion(account, payment, payment.getPurchasedAmount(), payment.getCurrency(), events);
+    }
+
+    protected Payment refundPaymentAndCheckForCompletion(final Account account, final Payment payment, final BigDecimal amount, final Currency currency, final NextEvent... events) {
         return doCallAndCheckForCompletion(new Function<Void, Payment>() {
             @Override
             public Payment apply(@Nullable final Void input) {
                 try {
-                    return paymentApi.createRefundWithPaymentControl(account, payment.getId(), payment.getPurchasedAmount(), payment.getCurrency(), UUID.randomUUID().toString(),
+                    return paymentApi.createRefundWithPaymentControl(account, payment.getId(), amount, currency, UUID.randomUUID().toString(),
                                                                      PLUGIN_PROPERTIES, PAYMENT_OPTIONS, refreshedCallContext());
                 } catch (final PaymentApiException e) {
                     fail(e.toString());
@@ -501,18 +508,21 @@ public class TestIntegrationBase extends BeatrixTestSuiteWithEmbeddedDB {
     }
 
     protected Payment refundPaymentWithInvoiceItemAdjAndCheckForCompletion(final Account account, final Payment payment, final Map<UUID, BigDecimal> iias, final NextEvent... events) {
+        return refundPaymentWithInvoiceItemAdjAndCheckForCompletion(account, payment, payment.getPurchasedAmount(), payment.getCurrency(), iias, events);
+    }
+
+    protected Payment refundPaymentWithInvoiceItemAdjAndCheckForCompletion(final Account account, final Payment payment, final BigDecimal amount, final Currency currency, final Map<UUID, BigDecimal> iias, final NextEvent... events) {
         return doCallAndCheckForCompletion(new Function<Void, Payment>() {
             @Override
             public Payment apply(@Nullable final Void input) {
-
-                final List<PluginProperty> properties = new ArrayList<PluginProperty>();
+                final Collection<PluginProperty> properties = new ArrayList<PluginProperty>();
                 final PluginProperty prop1 = new PluginProperty(InvoicePaymentControlPluginApi.PROP_IPCD_REFUND_WITH_ADJUSTMENTS, "true", false);
                 properties.add(prop1);
                 final PluginProperty prop2 = new PluginProperty(InvoicePaymentControlPluginApi.PROP_IPCD_REFUND_IDS_WITH_AMOUNT_KEY, iias, false);
                 properties.add(prop2);
 
                 try {
-                    return paymentApi.createRefundWithPaymentControl(account, payment.getId(), payment.getPurchasedAmount(), payment.getCurrency(), UUID.randomUUID().toString(),
+                    return paymentApi.createRefundWithPaymentControl(account, payment.getId(), amount, currency, UUID.randomUUID().toString(),
                                                                      properties, PAYMENT_OPTIONS, refreshedCallContext());
                 } catch (final PaymentApiException e) {
                     fail(e.toString());
@@ -522,18 +532,21 @@ public class TestIntegrationBase extends BeatrixTestSuiteWithEmbeddedDB {
         }, events);
     }
 
-    protected void createChargeBackAndCheckForCompletion(final Account account, final Payment payment, final NextEvent... events) {
-        doCallAndCheckForCompletion(new Function<Void, Void>() {
+    protected Payment createChargeBackAndCheckForCompletion(final Account account, final Payment payment, final NextEvent... events) {
+        return createChargeBackAndCheckForCompletion(account, payment, payment.getPurchasedAmount(), payment.getCurrency(), events);
+    }
+
+    protected Payment createChargeBackAndCheckForCompletion(final Account account, final Payment payment, final BigDecimal amount, final Currency currency, final NextEvent... events) {
+        return doCallAndCheckForCompletion(new Function<Void, Payment>() {
             @Override
-            public Void apply(@Nullable final Void input) {
+            public Payment apply(@Nullable final Void input) {
                 try {
-                    paymentApi.createChargebackWithPaymentControl(account, payment.getId(), payment.getPurchasedAmount(), payment.getCurrency(), UUID.randomUUID().toString(),
-                                                                  PAYMENT_OPTIONS, refreshedCallContext());
-                } catch (PaymentApiException e) {
+                    return paymentApi.createChargebackWithPaymentControl(account, payment.getId(), amount, currency, UUID.randomUUID().toString(),
+                                                                         PAYMENT_OPTIONS, refreshedCallContext());
+                } catch (final PaymentApiException e) {
                     fail(e.toString());
                     return null;
                 }
-                return null;
             }
         }, events);
     }
diff --git a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestIntegrationWithAutoInvoiceOffTag.java b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestIntegrationWithAutoInvoiceOffTag.java
index 403343f..7c34a8e 100644
--- a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestIntegrationWithAutoInvoiceOffTag.java
+++ b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestIntegrationWithAutoInvoiceOffTag.java
@@ -137,7 +137,7 @@ public class TestIntegrationWithAutoInvoiceOffTag extends TestIntegrationBase {
 
         add_AUTO_INVOICING_OFF_Tag(bpEntitlement.getSubscriptionBase().getBundleId(), ObjectType.BUNDLE);
 
-        busHandler.pushExpectedEvents(NextEvent.PHASE, NextEvent.PHASE, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
+        busHandler.pushExpectedEvents(NextEvent.PHASE, NextEvent.PHASE, NextEvent.NULL_INVOICE, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
         clock.addDays(40); // DAY 40 out of trial
         assertListenerStatus();
 
diff --git a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestIntegrationWithDifferentBillingPeriods.java b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestIntegrationWithDifferentBillingPeriods.java
index 55c5715..270f9a1 100644
--- a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestIntegrationWithDifferentBillingPeriods.java
+++ b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestIntegrationWithDifferentBillingPeriods.java
@@ -153,7 +153,7 @@ public class TestIntegrationWithDifferentBillingPeriods extends TestIntegrationB
         invoiceChecker.checkInvoice(invoices.get(2).getId(), callContext, toBeChecked);
 
         // Move to 1020-08-01
-        busHandler.pushExpectedEvents(NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
+        busHandler.pushExpectedEvents(NextEvent.NULL_INVOICE, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
         clock.addDays(20);
         clock.addMonths(2);
         assertListenerStatus();
@@ -199,7 +199,7 @@ public class TestIntegrationWithDifferentBillingPeriods extends TestIntegrationB
         // 2012-5-12
         clock.addDays(10);
 
-        busHandler.pushExpectedEvents(NextEvent.PAUSE, NextEvent.BLOCK, NextEvent.INVOICE);
+        busHandler.pushExpectedEvents(NextEvent.PAUSE, NextEvent.BLOCK, NextEvent.NULL_INVOICE, NextEvent.INVOICE);
         entitlementApi.pause(bpEntitlement.getBundleId(), clock.getUTCNow().toLocalDate(), ImmutableList.<PluginProperty>of(), callContext);
         assertListenerStatus();
 
@@ -218,7 +218,7 @@ public class TestIntegrationWithDifferentBillingPeriods extends TestIntegrationB
         // 2012-6-4
         clock.addDays(23);
 
-        busHandler.pushExpectedEvents(NextEvent.RESUME, NextEvent.BLOCK, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
+        busHandler.pushExpectedEvents(NextEvent.RESUME, NextEvent.BLOCK, NextEvent.NULL_INVOICE, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
         entitlementApi.resume(bpEntitlement.getBundleId(), clock.getUTCNow().toLocalDate(), ImmutableList.<PluginProperty>of(), callContext);
         assertListenerStatus();
 
@@ -230,7 +230,7 @@ public class TestIntegrationWithDifferentBillingPeriods extends TestIntegrationB
                 new ExpectedInvoiceItemCheck(new LocalDate(2012, 6, 4), new LocalDate(2012, 6, 4), InvoiceItemType.CBA_ADJ, new BigDecimal("-2327.62")));
         invoiceChecker.checkInvoice(invoices.get(3).getId(), callContext, toBeChecked);
 
-        busHandler.pushExpectedEvents(NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
+        busHandler.pushExpectedEvents(NextEvent.NULL_INVOICE, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
         clock.addYears(1);
         assertListenerStatus();
 
@@ -307,7 +307,7 @@ public class TestIntegrationWithDifferentBillingPeriods extends TestIntegrationB
                 new ExpectedInvoiceItemCheck(new LocalDate(2012, 5, 12), new LocalDate(2013, 5, 1), InvoiceItemType.REPAIR_ADJ, new BigDecimal("-2327.62")));
         invoiceChecker.checkInvoice(invoices.get(2).getId(), callContext, toBeChecked);
 
-        busHandler.pushExpectedEvents(NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
+        busHandler.pushExpectedEvents(NextEvent.NULL_INVOICE, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
         clock.addYears(1);
         assertListenerStatus();
 
diff --git a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestInvoicePayment.java b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestInvoicePayment.java
index fb08783..e79eb43 100644
--- a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestInvoicePayment.java
+++ b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestInvoicePayment.java
@@ -18,7 +18,10 @@
 package org.killbill.billing.beatrix.integration;
 
 import java.math.BigDecimal;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
+import java.util.UUID;
 
 import org.joda.time.DateTime;
 import org.joda.time.LocalDate;
@@ -35,6 +38,7 @@ import org.killbill.billing.entitlement.api.DefaultEntitlement;
 import org.killbill.billing.invoice.api.Invoice;
 import org.killbill.billing.invoice.api.InvoiceItem;
 import org.killbill.billing.invoice.api.InvoiceItemType;
+import org.killbill.billing.invoice.api.InvoicePaymentType;
 import org.killbill.billing.invoice.model.ExternalChargeInvoiceItem;
 import org.killbill.billing.payment.api.Payment;
 import org.killbill.billing.payment.api.PluginProperty;
@@ -74,11 +78,10 @@ public class TestInvoicePayment extends TestIntegrationBase {
 
         // Invoice is partially paid
         final Payment payment1 = paymentChecker.checkPayment(account.getId(), 1, callContext, new ExpectedPaymentCheck(new LocalDate(2012, 5, 31), new BigDecimal("249.95"), TransactionStatus.SUCCESS, invoice2.getId(), Currency.USD));
-        // TODO See https://github.com/killbill/killbill/issues/482
-        //Assert.assertEquals(payment1.getPurchasedAmount().compareTo(BigDecimal.TEN), 0);
+        Assert.assertEquals(payment1.getPurchasedAmount().compareTo(BigDecimal.TEN), 0);
         Assert.assertEquals(payment1.getTransactions().get(0).getProcessedAmount().compareTo(BigDecimal.TEN), 0);
         Assert.assertEquals(invoice2.getBalance().compareTo(new BigDecimal("239.95")), 0);
-        assertEquals(invoiceUserApi.getAccountBalance(account.getId(), callContext).compareTo(invoice2.getBalance()), 0);
+        Assert.assertEquals(invoiceUserApi.getAccountBalance(account.getId(), callContext).compareTo(invoice2.getBalance()), 0);
 
         // 2012-06-30
         addDaysAndCheckForCompletion(30, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
@@ -89,24 +92,244 @@ public class TestInvoicePayment extends TestIntegrationBase {
         // Invoice is fully paid
         final Payment payment2 = paymentChecker.checkPayment(account.getId(), 2, callContext, new ExpectedPaymentCheck(new LocalDate(2012, 6, 30), new BigDecimal("249.95"), TransactionStatus.SUCCESS, invoice3.getId(), Currency.USD));
         Assert.assertEquals(payment2.getPurchasedAmount().compareTo(new BigDecimal("249.95")), 0);
+        Assert.assertEquals(payment2.getTransactions().get(0).getProcessedAmount().compareTo(new BigDecimal("249.95")), 0);
         invoice2 = invoiceUserApi.getInvoice(invoice2.getId(), callContext);
         Assert.assertEquals(invoice2.getBalance().compareTo(new BigDecimal("239.95")), 0);
         Assert.assertEquals(invoice3.getBalance().compareTo(BigDecimal.ZERO), 0);
-        assertEquals(invoiceUserApi.getAccountBalance(account.getId(), callContext).compareTo(invoice2.getBalance()), 0);
+        Assert.assertEquals(invoiceUserApi.getAccountBalance(account.getId(), callContext).compareTo(invoice2.getBalance()), 0);
 
         // Fully pay the second invoice
         final Payment payment3 = createPaymentAndCheckForCompletion(account, invoice2, invoice2.getBalance(), account.getCurrency(), NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
         paymentChecker.checkPayment(account.getId(), 3, callContext, new ExpectedPaymentCheck(new LocalDate(2012, 6, 30), new BigDecimal("239.95"), TransactionStatus.SUCCESS, invoice2.getId(), Currency.USD));
         Assert.assertEquals(payment3.getPurchasedAmount().compareTo(new BigDecimal("239.95")), 0);
+        Assert.assertEquals(payment3.getTransactions().get(0).getProcessedAmount().compareTo(new BigDecimal("239.95")), 0);
         invoice2 = invoiceUserApi.getInvoice(invoice2.getId(), callContext);
         invoice3 = invoiceUserApi.getInvoice(invoice3.getId(), callContext);
         Assert.assertEquals(invoice2.getBalance().compareTo(BigDecimal.ZERO), 0);
         Assert.assertEquals(invoice3.getBalance().compareTo(BigDecimal.ZERO), 0);
-        assertEquals(invoiceUserApi.getAccountBalance(account.getId(), callContext).compareTo(BigDecimal.ZERO), 0);
+        Assert.assertEquals(invoiceUserApi.getAccountBalance(account.getId(), callContext).compareTo(BigDecimal.ZERO), 0);
     }
 
     @Test(groups = "slow")
-    public void testPartialPayments() throws Exception {
+    public void testPartialRefunds() throws Exception {
+        // 2012-05-01T00:03:42.000Z
+        clock.setTime(new DateTime(2012, 5, 1, 0, 3, 42, 0));
+
+        final AccountData accountData = getAccountData(0);
+        final Account account = createAccountWithNonOsgiPaymentMethod(accountData);
+        accountChecker.checkAccount(account.getId(), accountData, callContext);
+
+        final DefaultEntitlement baseEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "externalKey", "Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, NextEvent.CREATE, NextEvent.INVOICE);
+        invoiceChecker.checkInvoice(account.getId(), 1, callContext, new ExpectedInvoiceItemCheck(new LocalDate(2012, 5, 1), null, InvoiceItemType.FIXED, new BigDecimal("0")));
+        invoiceChecker.checkChargedThroughDate(baseEntitlement.getId(), new LocalDate(2012, 5, 1), callContext);
+
+        // 2012-05-31 => DAY 30 have to get out of trial {I0, P0}
+        addDaysAndCheckForCompletion(30, NextEvent.PHASE, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
+
+        Invoice invoice2 = invoiceChecker.checkInvoice(account.getId(), 2, callContext, new ExpectedInvoiceItemCheck(new LocalDate(2012, 5, 31), new LocalDate(2012, 6, 30), InvoiceItemType.RECURRING, new BigDecimal("249.95")));
+        invoiceChecker.checkChargedThroughDate(baseEntitlement.getId(), new LocalDate(2012, 6, 30), callContext);
+
+        // Invoice is fully paid
+        Payment payment1 = paymentChecker.checkPayment(account.getId(), 1, callContext, new ExpectedPaymentCheck(new LocalDate(2012, 5, 31), new BigDecimal("249.95"), TransactionStatus.SUCCESS, invoice2.getId(), Currency.USD));
+        Assert.assertEquals(payment1.getPurchasedAmount().compareTo(new BigDecimal("249.95")), 0);
+        Assert.assertEquals(payment1.getRefundedAmount().compareTo(BigDecimal.ZERO), 0);
+        Assert.assertEquals(payment1.getTransactions().get(0).getProcessedAmount().compareTo(new BigDecimal("249.95")), 0);
+        Assert.assertEquals(invoice2.getBalance().compareTo(BigDecimal.ZERO), 0);
+        Assert.assertEquals(invoiceUserApi.getAccountBalance(account.getId(), callContext).compareTo(invoice2.getBalance()), 0);
+
+        // Trigger first partial refund ($1), no adjustment
+        payment1 = refundPaymentAndCheckForCompletion(account, payment1, BigDecimal.TEN, Currency.USD, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
+        Assert.assertEquals(payment1.getPurchasedAmount().compareTo(new BigDecimal("249.95")), 0);
+        Assert.assertEquals(payment1.getRefundedAmount().compareTo(BigDecimal.TEN), 0);
+        Assert.assertEquals(payment1.getTransactions().size(), 2);
+        Assert.assertEquals(payment1.getTransactions().get(0).getAmount().compareTo(new BigDecimal("249.95")), 0);
+        Assert.assertEquals(payment1.getTransactions().get(0).getProcessedAmount().compareTo(new BigDecimal("249.95")), 0);
+        Assert.assertEquals(payment1.getTransactions().get(1).getAmount().compareTo(BigDecimal.TEN), 0);
+        Assert.assertEquals(payment1.getTransactions().get(1).getProcessedAmount().compareTo(BigDecimal.TEN), 0);
+        invoice2 = invoiceUserApi.getInvoice(invoice2.getId(), callContext);
+        Assert.assertEquals(invoice2.getBalance().compareTo(BigDecimal.TEN), 0);
+        Assert.assertEquals(invoiceUserApi.getAccountBalance(account.getId(), callContext).compareTo(invoice2.getBalance()), 0);
+
+        // Trigger second partial refund ($1), with item adjustment
+        final Map<UUID, BigDecimal> iias = new HashMap<UUID, BigDecimal>();
+        iias.put(invoice2.getInvoiceItems().get(0).getId(), BigDecimal.ONE);
+        payment1 = refundPaymentWithInvoiceItemAdjAndCheckForCompletion(account, payment1, BigDecimal.ONE, Currency.USD, iias, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT, NextEvent.INVOICE_ADJUSTMENT);
+        Assert.assertEquals(payment1.getPurchasedAmount().compareTo(new BigDecimal("249.95")), 0);
+        Assert.assertEquals(payment1.getRefundedAmount().compareTo(new BigDecimal("11")), 0);
+        Assert.assertEquals(payment1.getTransactions().size(), 3);
+        Assert.assertEquals(payment1.getTransactions().get(0).getAmount().compareTo(new BigDecimal("249.95")), 0);
+        Assert.assertEquals(payment1.getTransactions().get(0).getProcessedAmount().compareTo(new BigDecimal("249.95")), 0);
+        Assert.assertEquals(payment1.getTransactions().get(1).getAmount().compareTo(BigDecimal.TEN), 0);
+        Assert.assertEquals(payment1.getTransactions().get(1).getProcessedAmount().compareTo(BigDecimal.TEN), 0);
+        Assert.assertEquals(payment1.getTransactions().get(2).getAmount().compareTo(BigDecimal.ONE), 0);
+        Assert.assertEquals(payment1.getTransactions().get(2).getProcessedAmount().compareTo(BigDecimal.ONE), 0);
+        invoice2 = invoiceUserApi.getInvoice(invoice2.getId(), callContext);
+        Assert.assertEquals(invoice2.getBalance().compareTo(BigDecimal.TEN), 0);
+        Assert.assertEquals(invoiceUserApi.getAccountBalance(account.getId(), callContext).compareTo(invoice2.getBalance()), 0);
+
+        // Trigger third partial refund ($10), with item adjustment
+        iias.put(invoice2.getInvoiceItems().get(0).getId(), BigDecimal.TEN);
+        payment1 = refundPaymentWithInvoiceItemAdjAndCheckForCompletion(account, payment1, BigDecimal.TEN, Currency.USD, iias, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT, NextEvent.INVOICE_ADJUSTMENT);
+        Assert.assertEquals(payment1.getPurchasedAmount().compareTo(new BigDecimal("249.95")), 0);
+        Assert.assertEquals(payment1.getRefundedAmount().compareTo(new BigDecimal("21")), 0);
+        Assert.assertEquals(payment1.getTransactions().size(), 4);
+        Assert.assertEquals(payment1.getTransactions().get(0).getAmount().compareTo(new BigDecimal("249.95")), 0);
+        Assert.assertEquals(payment1.getTransactions().get(0).getProcessedAmount().compareTo(new BigDecimal("249.95")), 0);
+        Assert.assertEquals(payment1.getTransactions().get(1).getAmount().compareTo(BigDecimal.TEN), 0);
+        Assert.assertEquals(payment1.getTransactions().get(1).getProcessedAmount().compareTo(BigDecimal.TEN), 0);
+        Assert.assertEquals(payment1.getTransactions().get(2).getAmount().compareTo(BigDecimal.ONE), 0);
+        Assert.assertEquals(payment1.getTransactions().get(2).getProcessedAmount().compareTo(BigDecimal.ONE), 0);
+        Assert.assertEquals(payment1.getTransactions().get(3).getAmount().compareTo(BigDecimal.TEN), 0);
+        Assert.assertEquals(payment1.getTransactions().get(3).getProcessedAmount().compareTo(BigDecimal.TEN), 0);
+        invoice2 = invoiceUserApi.getInvoice(invoice2.getId(), callContext);
+        Assert.assertEquals(invoice2.getBalance().compareTo(BigDecimal.TEN), 0);
+        Assert.assertEquals(invoiceUserApi.getAccountBalance(account.getId(), callContext).compareTo(invoice2.getBalance()), 0);
+    }
+
+    @Test(groups = "slow")
+    public void testPartialPaymentByPaymentPluginThenChargeback() throws Exception {
+        // 2012-05-01T00:03:42.000Z
+        clock.setTime(new DateTime(2012, 5, 1, 0, 3, 42, 0));
+
+        final AccountData accountData = getAccountData(0);
+        final Account account = createAccountWithNonOsgiPaymentMethod(accountData);
+        accountChecker.checkAccount(account.getId(), accountData, callContext);
+
+        final DefaultEntitlement baseEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "externalKey", "Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, NextEvent.CREATE, NextEvent.INVOICE);
+        invoiceChecker.checkInvoice(account.getId(), 1, callContext, new ExpectedInvoiceItemCheck(new LocalDate(2012, 5, 1), null, InvoiceItemType.FIXED, new BigDecimal("0")));
+        invoiceChecker.checkChargedThroughDate(baseEntitlement.getId(), new LocalDate(2012, 5, 1), callContext);
+
+        // Trigger a partial payment on the next invoice
+        paymentPlugin.overrideNextProcessedAmount(BigDecimal.TEN);
+
+        // 2012-05-31 => DAY 30 have to get out of trial {I0, P0}
+        addDaysAndCheckForCompletion(30, NextEvent.PHASE, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
+
+        Invoice invoice2 = invoiceChecker.checkInvoice(account.getId(), 2, callContext, new ExpectedInvoiceItemCheck(new LocalDate(2012, 5, 31), new LocalDate(2012, 6, 30), InvoiceItemType.RECURRING, new BigDecimal("249.95")));
+        invoiceChecker.checkChargedThroughDate(baseEntitlement.getId(), new LocalDate(2012, 6, 30), callContext);
+
+        // Invoice is partially paid
+        Payment payment1 = paymentChecker.checkPayment(account.getId(), 1, callContext, new ExpectedPaymentCheck(new LocalDate(2012, 5, 31), new BigDecimal("249.95"), TransactionStatus.SUCCESS, invoice2.getId(), Currency.USD));
+        Assert.assertEquals(payment1.getPurchasedAmount().compareTo(BigDecimal.TEN), 0);
+        Assert.assertEquals(payment1.getTransactions().size(), 1);
+        Assert.assertEquals(payment1.getTransactions().get(0).getProcessedAmount().compareTo(BigDecimal.TEN), 0);
+        Assert.assertEquals(invoice2.getBalance().compareTo(new BigDecimal("239.95")), 0);
+        Assert.assertEquals(invoiceUserApi.getAccountBalance(account.getId(), callContext).compareTo(invoice2.getBalance()), 0);
+
+        // Trigger chargeback
+        payment1 = createChargeBackAndCheckForCompletion(account, payment1, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
+        Assert.assertEquals(payment1.getPurchasedAmount().compareTo(BigDecimal.TEN), 0);
+        Assert.assertEquals(payment1.getTransactions().size(), 2);
+        Assert.assertEquals(payment1.getTransactions().get(0).getAmount().compareTo(new BigDecimal("249.95")), 0);
+        Assert.assertEquals(payment1.getTransactions().get(0).getProcessedAmount().compareTo(BigDecimal.TEN), 0);
+        Assert.assertEquals(payment1.getTransactions().get(1).getAmount().compareTo(BigDecimal.TEN), 0);
+        Assert.assertEquals(payment1.getTransactions().get(1).getProcessedAmount().compareTo(BigDecimal.TEN), 0);
+        invoice2 = invoiceUserApi.getInvoice(invoice2.getId(), callContext);
+        Assert.assertEquals(invoice2.getBalance().compareTo(new BigDecimal("249.95")), 0);
+        Assert.assertEquals(invoiceUserApi.getAccountBalance(account.getId(), callContext).compareTo(invoice2.getBalance()), 0);
+    }
+
+    @Test(groups = "slow")
+    public void testAUTO_PAY_OFFThenPartialPayment() throws Exception {
+        // 2012-05-01T00:03:42.000Z
+        clock.setTime(new DateTime(2012, 5, 1, 0, 3, 42, 0));
+
+        final AccountData accountData = getAccountData(0);
+        final Account account = createAccountWithNonOsgiPaymentMethod(accountData);
+        accountChecker.checkAccount(account.getId(), accountData, callContext);
+
+        final DefaultEntitlement baseEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "externalKey", "Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, NextEvent.CREATE, NextEvent.INVOICE);
+        invoiceChecker.checkInvoice(account.getId(), 1, callContext, new ExpectedInvoiceItemCheck(new LocalDate(2012, 5, 1), null, InvoiceItemType.FIXED, new BigDecimal("0")));
+        invoiceChecker.checkChargedThroughDate(baseEntitlement.getId(), new LocalDate(2012, 5, 1), callContext);
+
+        // Put the account in AUTO_PAY_OFF to make sure payment system does not try to pay the initial invoice
+        add_AUTO_PAY_OFF_Tag(account.getId(), ObjectType.ACCOUNT);
+
+        // 2012-05-31 => DAY 30 have to get out of trial {I0, P0}
+        addDaysAndCheckForCompletion(30, NextEvent.PHASE, NextEvent.INVOICE);
+
+        Invoice invoice2 = invoiceChecker.checkInvoice(account.getId(), 2, callContext, new ExpectedInvoiceItemCheck(new LocalDate(2012, 5, 31), new LocalDate(2012, 6, 30), InvoiceItemType.RECURRING, new BigDecimal("249.95")));
+        invoiceChecker.checkChargedThroughDate(baseEntitlement.getId(), new LocalDate(2012, 6, 30), callContext);
+
+        // Invoice is not paid
+        Assert.assertEquals(paymentApi.getAccountPayments(account.getId(), false, ImmutableList.<PluginProperty>of(), callContext).size(), 0);
+        Assert.assertEquals(invoice2.getBalance().compareTo(new BigDecimal("249.95")), 0);
+        Assert.assertEquals(invoiceUserApi.getAccountBalance(account.getId(), callContext).compareTo(invoice2.getBalance()), 0);
+
+        // Trigger partial payment
+        final Payment payment1 = createPaymentAndCheckForCompletion(account, invoice2, BigDecimal.TEN, account.getCurrency(), NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
+        paymentChecker.checkPayment(account.getId(), 1, callContext, new ExpectedPaymentCheck(new LocalDate(2012, 5, 31), BigDecimal.TEN, TransactionStatus.SUCCESS, invoice2.getId(), Currency.USD));
+        Assert.assertEquals(payment1.getTransactions().size(), 1);
+        Assert.assertEquals(payment1.getPurchasedAmount().compareTo(BigDecimal.TEN), 0);
+        Assert.assertEquals(payment1.getTransactions().get(0).getProcessedAmount().compareTo(BigDecimal.TEN), 0);
+        invoice2 = invoiceUserApi.getInvoice(invoice2.getId(), callContext);
+        Assert.assertEquals(invoice2.getBalance().compareTo(new BigDecimal("239.95")), 0);
+        Assert.assertEquals(invoiceUserApi.getAccountBalance(account.getId(), callContext).compareTo(invoice2.getBalance()), 0);
+
+        // Remove AUTO_PAY_OFF and verify the invoice is fully paid
+        remove_AUTO_PAY_OFF_Tag(account.getId(), ObjectType.ACCOUNT, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
+        final Payment payment2 = paymentChecker.checkPayment(account.getId(), 2, callContext, new ExpectedPaymentCheck(new LocalDate(2012, 5, 31), new BigDecimal("239.95"), TransactionStatus.SUCCESS, invoice2.getId(), Currency.USD));
+        Assert.assertEquals(payment2.getTransactions().size(), 1);
+        Assert.assertEquals(payment2.getPurchasedAmount().compareTo(new BigDecimal("239.95")), 0);
+        Assert.assertEquals(payment2.getTransactions().get(0).getProcessedAmount().compareTo(new BigDecimal("239.95")), 0);
+        invoice2 = invoiceUserApi.getInvoice(invoice2.getId(), callContext);
+        Assert.assertEquals(invoice2.getBalance().compareTo(BigDecimal.ZERO), 0);
+        Assert.assertEquals(invoiceUserApi.getAccountBalance(account.getId(), callContext).compareTo(invoice2.getBalance()), 0);
+    }
+
+    @Test(groups = "slow")
+    public void testPaymentDifferentCurrencyByPaymentPlugin() throws Exception {
+        // 2012-05-01T00:03:42.000Z
+        clock.setTime(new DateTime(2012, 5, 1, 0, 3, 42, 0));
+
+        final AccountData accountData = getAccountData(0);
+        final Account account = createAccountWithNonOsgiPaymentMethod(accountData);
+        accountChecker.checkAccount(account.getId(), accountData, callContext);
+
+        final DefaultEntitlement baseEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "externalKey", "Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, NextEvent.CREATE, NextEvent.INVOICE);
+        invoiceChecker.checkInvoice(account.getId(), 1, callContext, new ExpectedInvoiceItemCheck(new LocalDate(2012, 5, 1), null, InvoiceItemType.FIXED, new BigDecimal("0")));
+        invoiceChecker.checkChargedThroughDate(baseEntitlement.getId(), new LocalDate(2012, 5, 1), callContext);
+
+        // Trigger a payment on the next invoice with a different currency ($249.95 <-> 225.44€)
+        paymentPlugin.overrideNextProcessedAmount(new BigDecimal("225.44"));
+        paymentPlugin.overrideNextProcessedCurrency(Currency.EUR);
+
+        // 2012-05-31 => DAY 30 have to get out of trial {I0, P0}
+        addDaysAndCheckForCompletion(30, NextEvent.PHASE, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
+
+        Invoice invoice2 = invoiceChecker.checkInvoice(account.getId(), 2, callContext, new ExpectedInvoiceItemCheck(new LocalDate(2012, 5, 31), new LocalDate(2012, 6, 30), InvoiceItemType.RECURRING, new BigDecimal("249.95")));
+        invoiceChecker.checkChargedThroughDate(baseEntitlement.getId(), new LocalDate(2012, 6, 30), callContext);
+
+        // Invoice is fully paid
+        Payment payment1 = paymentChecker.checkPayment(account.getId(), 1, callContext, new ExpectedPaymentCheck(new LocalDate(2012, 5, 31), new BigDecimal("249.95"), TransactionStatus.SUCCESS, invoice2.getId(), Currency.USD));
+        Assert.assertEquals(payment1.getPurchasedAmount().compareTo(new BigDecimal("249.95")), 0);
+        Assert.assertEquals(payment1.getTransactions().size(), 1);
+        Assert.assertEquals(payment1.getTransactions().get(0).getAmount().compareTo(new BigDecimal("249.95")), 0);
+        Assert.assertEquals(payment1.getTransactions().get(0).getCurrency(), Currency.USD);
+        Assert.assertEquals(payment1.getTransactions().get(0).getProcessedAmount().compareTo(new BigDecimal("225.44")), 0);
+        Assert.assertEquals(payment1.getTransactions().get(0).getProcessedCurrency(), Currency.EUR);
+        Assert.assertEquals(invoice2.getBalance().compareTo(BigDecimal.ZERO), 0);
+        Assert.assertEquals(invoiceUserApi.getAccountBalance(account.getId(), callContext).compareTo(BigDecimal.ZERO), 0);
+
+        // Trigger chargeback in the original currency
+        payment1 = createChargeBackAndCheckForCompletion(account, payment1, new BigDecimal("225.44"), Currency.EUR, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
+        Assert.assertEquals(payment1.getPurchasedAmount().compareTo(new BigDecimal("249.95")), 0);
+        Assert.assertEquals(payment1.getTransactions().size(), 2);
+        Assert.assertEquals(payment1.getTransactions().get(0).getAmount().compareTo(new BigDecimal("249.95")), 0);
+        Assert.assertEquals(payment1.getTransactions().get(0).getCurrency(), Currency.USD);
+        Assert.assertEquals(payment1.getTransactions().get(0).getProcessedAmount().compareTo(new BigDecimal("225.44")), 0);
+        Assert.assertEquals(payment1.getTransactions().get(0).getProcessedCurrency(), Currency.EUR);
+        Assert.assertEquals(payment1.getTransactions().get(1).getAmount().compareTo(new BigDecimal("225.44")), 0);
+        Assert.assertEquals(payment1.getTransactions().get(1).getCurrency(), Currency.EUR);
+        Assert.assertEquals(payment1.getTransactions().get(1).getProcessedAmount().compareTo(new BigDecimal("225.44")), 0);
+        Assert.assertEquals(payment1.getTransactions().get(1).getProcessedCurrency(), Currency.EUR);
+        invoice2 = invoiceUserApi.getInvoice(invoice2.getId(), callContext);
+        Assert.assertEquals(invoice2.getBalance().compareTo(new BigDecimal("249.95")), 0);
+        Assert.assertEquals(invoiceUserApi.getAccountBalance(account.getId(), callContext).compareTo(invoice2.getBalance()), 0);
+    }
+
+    @Test(groups = "slow")
+    public void testPartialRefundsOnPartialPayments() throws Exception {
         final AccountData accountData = getAccountData(1);
         final Account account = createAccountWithNonOsgiPaymentMethod(accountData);
         accountChecker.checkAccount(account.getId(), accountData, callContext);
@@ -120,8 +343,9 @@ public class TestInvoicePayment extends TestIntegrationBase {
         final InvoiceItem item1 = invoiceUserApi.insertExternalCharges(account.getId(), clock.getUTCToday(), ImmutableList.<InvoiceItem>of(externalCharge), callContext).get(0);
         assertListenerStatus();
 
+        // Trigger first partial payment ($4) on first invoice
         final Invoice invoice = invoiceUserApi.getInvoice(item1.getInvoiceId(), callContext);
-        final Payment payment1 = createPaymentAndCheckForCompletion(account, invoice, new BigDecimal("4.00"), account.getCurrency(), NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
+        Payment payment1 = createPaymentAndCheckForCompletion(account, invoice, new BigDecimal("4.00"), account.getCurrency(), NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
 
         Invoice invoice1 = invoiceUserApi.getInvoice(item1.getInvoiceId(), callContext);
         assertTrue(invoice1.getBalance().compareTo(new BigDecimal("6.00")) == 0);
@@ -131,7 +355,8 @@ public class TestInvoicePayment extends TestIntegrationBase {
         BigDecimal accountBalance = invoiceUserApi.getAccountBalance(account.getId(), callContext);
         assertTrue(accountBalance.compareTo(new BigDecimal("6.00")) == 0);
 
-        final Payment payment2 = createPaymentAndCheckForCompletion(account, invoice, new BigDecimal("6.00"), account.getCurrency(), NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
+        // Trigger second partial payment ($6) on first invoice
+        Payment payment2 = createPaymentAndCheckForCompletion(account, invoice, new BigDecimal("6.00"), account.getCurrency(), NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
 
         invoice1 = invoiceUserApi.getInvoice(item1.getInvoiceId(), callContext);
         assertTrue(invoice1.getBalance().compareTo(BigDecimal.ZERO) == 0);
@@ -141,27 +366,56 @@ public class TestInvoicePayment extends TestIntegrationBase {
         accountBalance = invoiceUserApi.getAccountBalance(account.getId(), callContext);
         assertTrue(accountBalance.compareTo(BigDecimal.ZERO) == 0);
 
-/*
-        This does not work since item is paid across multiple payments and so the mount is bigger than the payment.
-
-        // Now, issue refund with item adjustment on first invoice/item
-        paymentApi.createRefundWithItemsAdjustments(account, payment1.getId(), Sets.<UUID>newHashSet(item1.getId()), callContext);
-
+        // Refund first payment with item adjustment
+        final Map<UUID, BigDecimal> iias = new HashMap<UUID, BigDecimal>();
+        iias.put(item1.getId(), new BigDecimal("4.00"));
+        payment1 = refundPaymentWithInvoiceItemAdjAndCheckForCompletion(account, payment1, new BigDecimal("4.00"), Currency.USD, iias, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT, NextEvent.INVOICE_ADJUSTMENT);
         invoice1 = invoiceUserApi.getInvoice(item1.getInvoiceId(), callContext);
         assertTrue(invoice1.getBalance().compareTo(BigDecimal.ZERO) == 0);
-
         accountBalance = invoiceUserApi.getAccountBalance(account.getId(), callContext);
         assertTrue(accountBalance.compareTo(BigDecimal.ZERO) == 0);
 
-*/
-
-        refundPaymentAndCheckForCompletion(account, payment1, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
-
+        // Refund second payment with item adjustment
+        iias.put(item1.getId(), new BigDecimal("6.00"));
+        payment2 = refundPaymentWithInvoiceItemAdjAndCheckForCompletion(account, payment2, new BigDecimal("6.00"), Currency.USD, iias, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT, NextEvent.INVOICE_ADJUSTMENT);
         invoice1 = invoiceUserApi.getInvoice(item1.getInvoiceId(), callContext);
-        assertTrue(invoice1.getBalance().compareTo(new BigDecimal("4.00")) == 0);
-
+        assertTrue(invoice1.getBalance().compareTo(BigDecimal.ZERO) == 0);
         accountBalance = invoiceUserApi.getAccountBalance(account.getId(), callContext);
-        assertTrue(accountBalance.compareTo(new BigDecimal("4.00")) == 0);
+        assertTrue(accountBalance.compareTo(BigDecimal.ZERO) == 0);
+
+        Assert.assertEquals(invoice1.getPayments().size(), 4);
+
+        // Verify links for payment 1
+        Assert.assertEquals(invoice1.getPayments().get(0).getAmount().compareTo(new BigDecimal("4.00")), 0);
+        Assert.assertNull(invoice1.getPayments().get(0).getLinkedInvoicePaymentId());
+        Assert.assertNull(invoice1.getPayments().get(0).getPaymentCookieId());
+        Assert.assertEquals(invoice1.getPayments().get(0).getPaymentId(), payment1.getId());
+        Assert.assertEquals(invoice1.getPayments().get(0).getType(), InvoicePaymentType.ATTEMPT);
+        Assert.assertTrue(invoice1.getPayments().get(0).isSuccess());
+
+        // Verify links for payment 2
+        Assert.assertEquals(invoice1.getPayments().get(1).getAmount().compareTo(new BigDecimal("6.00")), 0);
+        Assert.assertNull(invoice1.getPayments().get(1).getLinkedInvoicePaymentId());
+        Assert.assertNull(invoice1.getPayments().get(1).getPaymentCookieId());
+        Assert.assertEquals(invoice1.getPayments().get(1).getPaymentId(), payment2.getId());
+        Assert.assertEquals(invoice1.getPayments().get(1).getType(), InvoicePaymentType.ATTEMPT);
+        Assert.assertTrue(invoice1.getPayments().get(1).isSuccess());
+
+        // Verify links for refund 1
+        Assert.assertEquals(invoice1.getPayments().get(2).getAmount().compareTo(new BigDecimal("-4.00")), 0);
+        Assert.assertEquals(invoice1.getPayments().get(2).getLinkedInvoicePaymentId(), invoice1.getPayments().get(0).getId());
+        Assert.assertEquals(invoice1.getPayments().get(2).getPaymentCookieId(), payment1.getTransactions().get(1).getExternalKey());
+        Assert.assertEquals(invoice1.getPayments().get(2).getPaymentId(), payment1.getId());
+        Assert.assertEquals(invoice1.getPayments().get(2).getType(), InvoicePaymentType.REFUND);
+        Assert.assertTrue(invoice1.getPayments().get(2).isSuccess());
+
+        // Verify links for refund 2
+        Assert.assertEquals(invoice1.getPayments().get(3).getAmount().compareTo(new BigDecimal("-6.00")), 0);
+        Assert.assertEquals(invoice1.getPayments().get(3).getLinkedInvoicePaymentId(), invoice1.getPayments().get(1).getId());
+        Assert.assertEquals(invoice1.getPayments().get(3).getPaymentCookieId(), payment2.getTransactions().get(1).getExternalKey());
+        Assert.assertEquals(invoice1.getPayments().get(3).getPaymentId(), payment2.getId());
+        Assert.assertEquals(invoice1.getPayments().get(3).getType(), InvoicePaymentType.REFUND);
+        Assert.assertTrue(invoice1.getPayments().get(3).isSuccess());
     }
 
     @Test(groups = "slow")
@@ -189,6 +443,8 @@ public class TestInvoicePayment extends TestIntegrationBase {
         assertTrue(invoice1.getPaidAmount().compareTo(BigDecimal.ZERO) == 0);
         assertTrue(invoice1.getChargedAmount().compareTo(new BigDecimal("249.95")) == 0);
         assertEquals(invoice1.getPayments().size(), 1);
+        assertEquals(invoice1.getPayments().get(0).getAmount().compareTo(BigDecimal.ZERO), 0);
+        assertEquals(invoice1.getPayments().get(0).getCurrency(), Currency.USD);
         assertFalse(invoice1.getPayments().get(0).isSuccess());
 
         final BigDecimal accountBalance1 = invoiceUserApi.getAccountBalance(account.getId(), callContext);
@@ -196,6 +452,12 @@ public class TestInvoicePayment extends TestIntegrationBase {
 
         final List<Payment> payments = paymentApi.getAccountPayments(account.getId(), false, ImmutableList.<PluginProperty>of(), callContext);
         assertEquals(payments.size(), 1);
+        assertEquals(payments.get(0).getPurchasedAmount().compareTo(BigDecimal.ZERO), 0);
+        assertEquals(payments.get(0).getTransactions().size(), 1);
+        assertEquals(payments.get(0).getTransactions().get(0).getAmount().compareTo(new BigDecimal("249.95")), 0);
+        assertEquals(payments.get(0).getTransactions().get(0).getCurrency(), Currency.USD);
+        assertEquals(payments.get(0).getTransactions().get(0).getProcessedAmount().compareTo(BigDecimal.ZERO), 0);
+        assertEquals(payments.get(0).getTransactions().get(0).getProcessedCurrency(), Currency.USD);
 
         // Trigger the payment retry
         busHandler.pushExpectedEvents(NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
@@ -211,5 +473,13 @@ public class TestInvoicePayment extends TestIntegrationBase {
 
         final BigDecimal accountBalance2 = invoiceUserApi.getAccountBalance(account.getId(), callContext);
         assertTrue(accountBalance2.compareTo(BigDecimal.ZERO) == 0);
+
+        final List<Payment> payments2 = paymentApi.getAccountPayments(account.getId(), false, ImmutableList.<PluginProperty>of(), callContext);
+        assertEquals(payments2.size(), 1);
+        assertEquals(payments2.get(0).getTransactions().size(), 2);
+        assertEquals(payments2.get(0).getTransactions().get(1).getAmount().compareTo(new BigDecimal("249.95")), 0);
+        assertEquals(payments2.get(0).getTransactions().get(1).getCurrency(), Currency.USD);
+        assertEquals(payments2.get(0).getTransactions().get(1).getProcessedAmount().compareTo(new BigDecimal("249.95")), 0);
+        assertEquals(payments2.get(0).getTransactions().get(1).getProcessedCurrency(), Currency.USD);
     }
 }
diff --git a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestSubscription.java b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestSubscription.java
index 7d6e4c8..3c5e59f 100644
--- a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestSubscription.java
+++ b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestSubscription.java
@@ -212,7 +212,7 @@ public class TestSubscription extends TestIntegrationBase {
         specifierList.add(addOnEntitlementSpecifier1);
         specifierList.add(addOnEntitlementSpecifier2);
 
-        busHandler.pushExpectedEvents(NextEvent.CREATE, NextEvent.CREATE, NextEvent.CREATE, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
+        busHandler.pushExpectedEvents(NextEvent.CREATE, NextEvent.CREATE, NextEvent.CREATE, NextEvent.NULL_INVOICE, NextEvent.NULL_INVOICE, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
         final Entitlement entitlement = entitlementApi.createBaseEntitlementWithAddOns(account.getId(), externalKey, specifierList, initialDate, ImmutableList.<PluginProperty>of(), callContext);
         assertListenerStatus();
         checkNoMoreInvoiceToGenerate(account);
@@ -252,7 +252,6 @@ public class TestSubscription extends TestIntegrationBase {
 
     @Test(groups = "slow")
     public void testCancelFutureSubscriptionWithPolicy() throws Exception {
-
         final LocalDate initialDate = new LocalDate(2015, 9, 1);
         clock.setDay(initialDate);
 
@@ -274,8 +273,7 @@ public class TestSubscription extends TestIntegrationBase {
         assertListenerStatus();
 
         // Move off trial and reach start/cancellation date
-        // NextEvent.INVOICE is required because of #467
-        busHandler.pushExpectedEvents(NextEvent.CREATE, NextEvent.BLOCK, NextEvent.CANCEL);
+        busHandler.pushExpectedEvents(NextEvent.CREATE, NextEvent.BLOCK, NextEvent.CANCEL, NextEvent.NULL_INVOICE, NextEvent.NULL_INVOICE);
         clock.addDays(30);
         assertListenerStatus();
 
@@ -286,7 +284,6 @@ public class TestSubscription extends TestIntegrationBase {
 
     @Test(groups = "slow")
     public void testCancelFutureSubscriptionWithRequestedDate() throws Exception {
-
         final LocalDate initialDate = new LocalDate(2015, 9, 1);
         clock.setDay(initialDate);
 
@@ -317,13 +314,12 @@ public class TestSubscription extends TestIntegrationBase {
         assertListenerStatus();
 
         // Move off trial and reach start/cancellation date
-        busHandler.pushExpectedEvents(NextEvent.CREATE, NextEvent.BLOCK, NextEvent.CANCEL);
+        busHandler.pushExpectedEvents(NextEvent.CREATE, NextEvent.BLOCK, NextEvent.CANCEL, NextEvent.NULL_INVOICE, NextEvent.NULL_INVOICE);
         clock.addDays(30);
         assertListenerStatus();
 
         // Just to make sure we really don't invoice for anything move to next month
         clock.addMonths(1);
         assertListenerStatus();
-
     }
 }
diff --git a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestWithTimeZones.java b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestWithTimeZones.java
index 24fa051..c044cf1 100644
--- a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestWithTimeZones.java
+++ b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestWithTimeZones.java
@@ -52,10 +52,8 @@ public class TestWithTimeZones extends TestIntegrationBase {
     // Verify that recurring invoice items are correctly computed although we went through and out of daylight saving transitions
     @Test(groups = "slow")
     public void testWithDayLightSaving() throws Exception {
-
         // Start with a date in daylight saving period  and make sure we use a time of 8 hour so that we we reach standard time
         // the next month where the difference is 9 hours, a transformation from DateTime to LocalDate with the account time zone would bring us a day earlier
-        //
         clock.setTime(new DateTime("2015-09-01T08:01:01.000Z"));
 
         final DateTimeZone tz = DateTimeZone.forID("America/Juneau");
@@ -76,9 +74,9 @@ public class TestWithTimeZones extends TestIntegrationBase {
 
         final List<ExpectedInvoiceItemCheck> expectedInvoices = new ArrayList<ExpectedInvoiceItemCheck>();
 
-        TestDryRunArguments dryRun = new TestDryRunArguments(DryRunType.SUBSCRIPTION_ACTION, "Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, null, null,
-                                                             SubscriptionEventType.START_BILLING, null, null, clock.getUTCNow(), null);
-        Invoice dryRunInvoice = invoiceUserApi.triggerInvoiceGeneration(account.getId(), clock.getUTCToday(), dryRun, callContext);
+        final TestDryRunArguments dryRun = new TestDryRunArguments(DryRunType.SUBSCRIPTION_ACTION, "Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, null, null,
+                                                                   SubscriptionEventType.START_BILLING, null, null, clock.getUTCNow(), null);
+        final Invoice dryRunInvoice = invoiceUserApi.triggerInvoiceGeneration(account.getId(), clock.getUTCToday(), dryRun, callContext);
         expectedInvoices.add(new ExpectedInvoiceItemCheck(new LocalDate(2015, 9, 1), null, InvoiceItemType.FIXED, new BigDecimal("0")));
         invoiceChecker.checkInvoiceNoAudits(dryRunInvoice, callContext, expectedInvoices);
 
@@ -97,7 +95,7 @@ public class TestWithTimeZones extends TestIntegrationBase {
         LocalDate startDate = new LocalDate(2015, 11, 1);
         // We loop 18 times to go over a year and transitions several times between winter and summer (daylight saving)
         for (int i = 0; i < 18; i++) {
-            LocalDate endDate = startDate.plusMonths(1);
+            final LocalDate endDate = startDate.plusMonths(1);
 
             busHandler.pushExpectedEvents(NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
             clock.addMonths(1);
@@ -112,7 +110,6 @@ public class TestWithTimeZones extends TestIntegrationBase {
     // Verify cancellation logic when we exit daylight savind period
     @Test(groups = "slow")
     public void testCancellationFrom_PDT_to_PST() throws Exception {
-
         // Start with a date in daylight saving period (PDT) and make sure we use a time of 7 hour so that we we reach standard time (PST)
         // the next month where the difference is 8 hours, a transformation from DateTime to LocalDate with the account time zone would bring us a day earlier
         // (e.g new LocalDate("2015-12-01T07:01:01.000Z", tz) -> "2015-11-30.
@@ -147,23 +144,22 @@ public class TestWithTimeZones extends TestIntegrationBase {
         // Verify first entitlement is correctly cancelled on the right date
         Assert.assertEquals(entitlement.getEffectiveEndDate(), cancellationDate);
 
+        busHandler.pushExpectedEvent(NextEvent.NULL_INVOICE);
         // We move the clock to the date of the next invoice notification 2015-12-01 07:01:01 (invoice is using a fixed offset of 7 hours and all billing events are converted using that offset)
         clock.setTime(new DateTime("2015-12-01T07:01:02"));
-        // Unfortunately we did not plug NullInvoice events so we cannot synchronize on that (See https://github.com/killbill/killbill/issues/448)
         assertListenerStatus();
 
         // We now move the clock to the date of the cancellation (one hour later), which match the cancellation day from the client point of view
         //
-        // For curious reader, the reason why the time end up being '8:01:0' comes from (https://github.com/killbill/killbill-commons/blob/master/clock/src/main/java/org/killbill/clock/ClockUtil.java#L51):
+        // For the curious reader, the reason why the time end up being '8:01:0' comes from (https://github.com/killbill/killbill-commons/blob/master/clock/src/main/java/org/killbill/clock/ClockUtil.java#L51):
         // We compute a DateTime in the account timezone by specifying explicitly the year-month-day we want to end up in, and shoving *a time*. The reason why we end up on an 8:01:02 is not necessarily so important,
         // What's important is that by construction that DateTime is guaranteed to match a LocalDate of 2015-12-01
-        //
-        busHandler.pushExpectedEvents(NextEvent.CANCEL, NextEvent.BLOCK);
+        busHandler.pushExpectedEvents(NextEvent.CANCEL, NextEvent.BLOCK, NextEvent.NULL_INVOICE);
         clock.setTime(new DateTime("2015-12-01T08:01:02"));
         assertListenerStatus();
 
-        // Verify second that there was no repair (so the cancellation did correctly happen on the "2015-12-01"
-        List<Invoice> invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), callContext);
+        // Verify second that there was no repair (so the cancellation did correctly happen on the "2015-12-01")
+        final List<Invoice> invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), callContext);
         Assert.assertEquals(invoices.size(), 1);
     }
 
@@ -171,7 +167,6 @@ public class TestWithTimeZones extends TestIntegrationBase {
     // an offset of 8 hours and then go through 7 hours so anyway we would stay in the same day.
     @Test(groups = "slow")
     public void testCancellationFrom_PST_to_PDT() throws Exception {
-
         clock.setTime(new DateTime("2015-02-01T08:01:01.000Z"));
 
         final DateTimeZone tz = DateTimeZone.forID("America/Los_Angeles");
@@ -203,16 +198,13 @@ public class TestWithTimeZones extends TestIntegrationBase {
         // Verify first entitlement is correctly cancelled on the right date
         Assert.assertEquals(entitlement.getEffectiveEndDate(), cancellationDate);
 
-
         // We now move the clock to the date of the cancellation  which match the cancellation day from the client point of view
-        busHandler.pushExpectedEvents(NextEvent.CANCEL, NextEvent.BLOCK);
+        busHandler.pushExpectedEvents(NextEvent.CANCEL, NextEvent.BLOCK, NextEvent.NULL_INVOICE, NextEvent.NULL_INVOICE);
         clock.setTime(new DateTime("2015-03-01T08:01:02"));
         assertListenerStatus();
 
         // Verify second that there was no repair (so the cancellation did correctly happen on the "2015-12-01"
-        List<Invoice> invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), callContext);
+        final List<Invoice> invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), callContext);
         Assert.assertEquals(invoices.size(), 1);
-
     }
-
 }
diff --git a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/usage/TestConsumableInArrear.java b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/usage/TestConsumableInArrear.java
index 54107d1..80ccbac 100644
--- a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/usage/TestConsumableInArrear.java
+++ b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/usage/TestConsumableInArrear.java
@@ -60,7 +60,6 @@ public class TestConsumableInArrear extends TestIntegrationBase {
 
     @Test(groups = "slow")
     public void testWithNoUsageInPeriodAndOldUsage() throws Exception {
-
         final AccountData accountData = getAccountData(1);
         final Account account = createAccountWithNonOsgiPaymentMethod(accountData);
         accountChecker.checkAccount(account.getId(), accountData, callContext);
@@ -80,12 +79,12 @@ public class TestConsumableInArrear extends TestIntegrationBase {
         //
         // ADD ADD_ON ON THE SAME DAY
         //
-        final DefaultEntitlement aoSubscription = addAOEntitlementAndCheckForCompletion(bpSubscription.getBundleId(), "Bullets", ProductCategory.ADD_ON, BillingPeriod.NO_BILLING_PERIOD, NextEvent.CREATE);
+        final DefaultEntitlement aoSubscription = addAOEntitlementAndCheckForCompletion(bpSubscription.getBundleId(), "Bullets", ProductCategory.ADD_ON, BillingPeriod.NO_BILLING_PERIOD, NextEvent.CREATE, NextEvent.NULL_INVOICE);
 
         setUsage(aoSubscription.getId(), "bullets", new LocalDate(2012, 4, 1), 99L, callContext);
         setUsage(aoSubscription.getId(), "bullets", new LocalDate(2012, 4, 15), 100L, callContext);
 
-        busHandler.pushExpectedEvents(NextEvent.PHASE, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
+        busHandler.pushExpectedEvents(NextEvent.PHASE, NextEvent.NULL_INVOICE, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
         clock.addDays(30);
         assertListenerStatus();
 
@@ -93,9 +92,10 @@ public class TestConsumableInArrear extends TestIntegrationBase {
                                     new ExpectedInvoiceItemCheck(new LocalDate(2012, 5, 1), new LocalDate(2013, 5, 1), InvoiceItemType.RECURRING, new BigDecimal("2399.95")),
                                     new ExpectedInvoiceItemCheck(new LocalDate(2012, 4, 1), new LocalDate(2012, 5, 1), InvoiceItemType.USAGE, new BigDecimal("5.90")));
 
-        // We don't expect any invoice, but we want to give the system the time to verify there is nothing to do so we can fail
+        // We don't expect any invoice
+        busHandler.pushExpectedEvents(NextEvent.NULL_INVOICE);
         clock.addMonths(1);
-        Thread.sleep(1000);
+        assertListenerStatus();
 
         setUsage(aoSubscription.getId(), "bullets", new LocalDate(2012, 6, 1), 50L, callContext);
         setUsage(aoSubscription.getId(), "bullets", new LocalDate(2012, 6, 16), 300L, callContext);
@@ -128,7 +128,6 @@ public class TestConsumableInArrear extends TestIntegrationBase {
 
     @Test(groups = "slow")
     public void testWithCancellation() throws Exception {
-
         final AccountData accountData = getAccountData(1);
         final Account account = createAccountWithNonOsgiPaymentMethod(accountData);
         accountChecker.checkAccount(account.getId(), accountData, callContext);
@@ -148,12 +147,12 @@ public class TestConsumableInArrear extends TestIntegrationBase {
         //
         // ADD ADD_ON ON THE SAME DAY
         //
-        final DefaultEntitlement aoSubscription = addAOEntitlementAndCheckForCompletion(bpSubscription.getBundleId(), "Bullets", ProductCategory.ADD_ON, BillingPeriod.NO_BILLING_PERIOD, NextEvent.CREATE);
+        final DefaultEntitlement aoSubscription = addAOEntitlementAndCheckForCompletion(bpSubscription.getBundleId(), "Bullets", ProductCategory.ADD_ON, BillingPeriod.NO_BILLING_PERIOD, NextEvent.CREATE, NextEvent.NULL_INVOICE);
 
         setUsage(aoSubscription.getId(), "bullets", new LocalDate(2012, 4, 1), 99L, callContext);
         setUsage(aoSubscription.getId(), "bullets", new LocalDate(2012, 4, 15), 100L, callContext);
 
-        busHandler.pushExpectedEvents(NextEvent.PHASE, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
+        busHandler.pushExpectedEvents(NextEvent.PHASE, NextEvent.NULL_INVOICE, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
         clock.addDays(30);
         assertListenerStatus();
         invoiceChecker.checkInvoice(account.getId(), 2, callContext,
@@ -174,14 +173,13 @@ public class TestConsumableInArrear extends TestIntegrationBase {
         invoiceChecker.checkInvoice(account.getId(), 3, callContext,
                                     new ExpectedInvoiceItemCheck(new LocalDate(2012, 5, 1), new LocalDate(2012, 5, 28), InvoiceItemType.USAGE, new BigDecimal("5.90")));
 
+        busHandler.pushExpectedEvent(NextEvent.NULL_INVOICE);
         clock.addDays(4);
-        Thread.sleep(1000);
-
+        assertListenerStatus();
     }
 
     @Test(groups = "slow")
     public void testWithDayLightSaving() throws Exception {
-
         clock.setTime(new DateTime("2015-09-01T08:01:01.000Z"));
 
         final DateTimeZone tz = DateTimeZone.forID("America/Juneau");
@@ -212,20 +210,22 @@ public class TestConsumableInArrear extends TestIntegrationBase {
         //
         // ADD ADD_ON ON THE SAME DAY
         //
-        final DefaultEntitlement aoSubscription = addAOEntitlementAndCheckForCompletion(bpSubscription.getBundleId(), "Bullets", ProductCategory.ADD_ON, BillingPeriod.NO_BILLING_PERIOD, NextEvent.CREATE);
+        final DefaultEntitlement aoSubscription = addAOEntitlementAndCheckForCompletion(bpSubscription.getBundleId(), "Bullets", ProductCategory.ADD_ON, BillingPeriod.NO_BILLING_PERIOD, NextEvent.CREATE, NextEvent.NULL_INVOICE);
         assertListenerStatus();
 
-        busHandler.pushExpectedEvents(NextEvent.PHASE, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
+        busHandler.pushExpectedEvents(NextEvent.PHASE, NextEvent.NULL_INVOICE, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
         clock.addDays(30);
         assertListenerStatus();
         invoiceChecker.checkInvoice(account.getId(), 2, callContext,
                                     new ExpectedInvoiceItemCheck(new LocalDate(2015, 10, 1), new LocalDate(2016, 10, 1), InvoiceItemType.RECURRING, new BigDecimal("2399.95")));
 
         // 2015-11-1
+        busHandler.pushExpectedEvent(NextEvent.NULL_INVOICE);
         clock.addMonths(1);
         assertListenerStatus();
 
         // 2015-12-1
+        busHandler.pushExpectedEvent(NextEvent.NULL_INVOICE);
         clock.addMonths(1);
         assertListenerStatus();
 

catalog/pom.xml 2(+1 -1)

diff --git a/catalog/pom.xml b/catalog/pom.xml
index 714169b..e75310f 100644
--- a/catalog/pom.xml
+++ b/catalog/pom.xml
@@ -19,7 +19,7 @@
     <parent>
         <artifactId>killbill</artifactId>
         <groupId>org.kill-bill.billing</groupId>
-        <version>0.16.1-SNAPSHOT</version>
+        <version>0.16.2-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>killbill-catalog</artifactId>
diff --git a/catalog/src/main/java/org/killbill/billing/catalog/DefaultPlan.java b/catalog/src/main/java/org/killbill/billing/catalog/DefaultPlan.java
index 7cedb32..1955c5e 100644
--- a/catalog/src/main/java/org/killbill/billing/catalog/DefaultPlan.java
+++ b/catalog/src/main/java/org/killbill/billing/catalog/DefaultPlan.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:
  *
@@ -19,6 +21,8 @@ package org.killbill.billing.catalog;
 import java.net.URI;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
 import java.util.Date;
 import java.util.Iterator;
 
@@ -85,31 +89,24 @@ public class DefaultPlan extends ValidatingConfig<StandaloneCatalog> implements 
         this.effectiveDateForExistingSubscriptons = in.getEffectiveDateForExistingSubscriptons();
         this.product = (DefaultProduct) in.getProduct();
         this.initialPhases = new DefaultPlanPhase[in.getInitialPhases().length];
-        for (int i = 0; i< overrides.length - 1; i++) {
+        for (int i = 0; i < overrides.length - 1; i++) {
             final DefaultPlanPhase newPhase = new DefaultPlanPhase(this, in.getInitialPhases()[i], overrides[i]);
             initialPhases[i] = newPhase;
         }
         this.finalPhase = new DefaultPlanPhase(this, in.getFinalPhase(), overrides[overrides.length - 1]);
         this.priceList = in.getPriceList();
     }
-    /* (non-Javadoc)
-      * @see org.killbill.billing.catalog.IPlan#getEffectiveDateForExistingSubscriptons()
-      */
+
     @Override
     public Date getEffectiveDateForExistingSubscriptons() {
         return effectiveDateForExistingSubscriptons;
-    }    /* (non-Javadoc)
-	 * @see org.killbill.billing.catalog.IPlan#getPhases()
-	 */
+    }
 
     @Override
     public DefaultPlanPhase[] getInitialPhases() {
         return initialPhases;
     }
 
-    /* (non-Javadoc)
-	 * @see org.killbill.billing.catalog.IPlan#getProduct()
-	 */
     @Override
     public Product getProduct() {
         return product;
@@ -120,9 +117,6 @@ public class DefaultPlan extends ValidatingConfig<StandaloneCatalog> implements 
         return priceList;
     }
 
-    /* (non-Javadoc)
-      * @see org.killbill.billing.catalog.IPlan#getName()
-      */
     @Override
     public String getName() {
         return name;
@@ -163,9 +157,6 @@ public class DefaultPlan extends ValidatingConfig<StandaloneCatalog> implements 
         return finalPhase.getRecurring() != null ? finalPhase.getRecurring().getBillingPeriod() : BillingPeriod.NO_BILLING_PERIOD;
     }
 
-    /* (non-Javadoc)
-      * @see org.killbill.billing.catalog.IPlan#getPlansAllowedInBundle()
-      */
     @Override
     public int getPlansAllowedInBundle() {
         return plansAllowedInBundle;
@@ -176,10 +167,8 @@ public class DefaultPlan extends ValidatingConfig<StandaloneCatalog> implements 
       */
     @Override
     public Iterator<PlanPhase> getInitialPhaseIterator() {
-        final ArrayList<PlanPhase> list = new ArrayList<PlanPhase>();
-        for (final DefaultPlanPhase p : initialPhases) {
-            list.add(p);
-        }
+        final Collection<PlanPhase> list = new ArrayList<PlanPhase>();
+        Collections.addAll(list, initialPhases);
         return list.iterator();
     }
 
@@ -199,12 +188,10 @@ public class DefaultPlan extends ValidatingConfig<StandaloneCatalog> implements 
         this.priceList = findPriceListForPlan(catalog);
     }
 
-
-
     @Override
     public ValidationErrors validate(final StandaloneCatalog catalog, final ValidationErrors errors) {
         if (effectiveDateForExistingSubscriptons != null &&
-                catalog.getEffectiveDate().getTime() > effectiveDateForExistingSubscriptons.getTime()) {
+            catalog.getEffectiveDate().getTime() > effectiveDateForExistingSubscriptons.getTime()) {
             errors.add(new ValidationError(String.format("Price effective date %s is before catalog effective date '%s'",
                                                          effectiveDateForExistingSubscriptons,
                                                          catalog.getEffectiveDate().getTime()),
@@ -253,8 +240,8 @@ public class DefaultPlan extends ValidatingConfig<StandaloneCatalog> implements 
 
     @Override
     public DateTime dateOfFirstRecurringNonZeroCharge(final DateTime subscriptionStartDate, final PhaseType initialPhaseType) {
-        DateTime result = subscriptionStartDate.toDateTime();
-        boolean skipPhase = initialPhaseType == null ? false : true;
+        DateTime result = subscriptionStartDate;
+        boolean skipPhase = initialPhaseType != null;
         for (final PlanPhase phase : getAllPhases()) {
             if (skipPhase) {
                 if (phase.getPhaseType() != initialPhaseType) {
@@ -318,9 +305,9 @@ public class DefaultPlan extends ValidatingConfig<StandaloneCatalog> implements 
     @Override
     public String toString() {
         return "DefaultPlan [name=" + name + ", effectiveDateForExistingSubscriptons="
-                + effectiveDateForExistingSubscriptons + ", product=" + product + ", initialPhases="
-                + Arrays.toString(initialPhases) + ", finalPhase=" + finalPhase + ", plansAllowedInBundle="
-                + plansAllowedInBundle + "]";
+               + effectiveDateForExistingSubscriptons + ", product=" + product + ", initialPhases="
+               + Arrays.toString(initialPhases) + ", finalPhase=" + finalPhase + ", plansAllowedInBundle="
+               + plansAllowedInBundle + "]";
     }
 
     private DefaultPriceList findPriceListForPlan(final StandaloneCatalog catalog) {
diff --git a/catalog/src/main/java/org/killbill/billing/catalog/io/VersionedCatalogLoader.java b/catalog/src/main/java/org/killbill/billing/catalog/io/VersionedCatalogLoader.java
index 9b8df53..7bf1326 100644
--- a/catalog/src/main/java/org/killbill/billing/catalog/io/VersionedCatalogLoader.java
+++ b/catalog/src/main/java/org/killbill/billing/catalog/io/VersionedCatalogLoader.java
@@ -55,13 +55,10 @@ public class VersionedCatalogLoader implements CatalogLoader {
         this.internalCallContextFactory = internalCallContextFactory;
     }
 
-    /* (non-Javadoc)
-      * @see org.killbill.billing.catalog.io.ICatalogLoader#loadDefaultCatalog(java.lang.String)
-      */
     @Override
     public VersionedCatalog loadDefaultCatalog(final String uriString) throws CatalogApiException {
         try {
-            List<URI> xmlURIs;
+            final List<URI> xmlURIs;
             if (uriString.endsWith(XML_EXTENSION)) { // Assume its an xml file
                 xmlURIs = new ArrayList<URI>();
                 xmlURIs.add(new URI(uriString));
diff --git a/catalog/src/main/java/org/killbill/billing/catalog/StandaloneCatalogWithPriceOverride.java b/catalog/src/main/java/org/killbill/billing/catalog/StandaloneCatalogWithPriceOverride.java
index ac2326a..5fcece4 100644
--- a/catalog/src/main/java/org/killbill/billing/catalog/StandaloneCatalogWithPriceOverride.java
+++ b/catalog/src/main/java/org/killbill/billing/catalog/StandaloneCatalogWithPriceOverride.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
@@ -24,7 +24,6 @@ import java.util.regex.Matcher;
 
 import javax.annotation.Nullable;
 
-import org.joda.time.DateTime;
 import org.killbill.billing.callcontext.InternalCallContext;
 import org.killbill.billing.callcontext.InternalTenantContext;
 import org.killbill.billing.catalog.api.BillingActionPolicy;
@@ -141,15 +140,14 @@ public class StandaloneCatalogWithPriceOverride extends ValidatingConfig<Standal
         }
 
         final InternalCallContext internalCallContext = internalCallContextFactory.createInternalCallContext(overrides.getCallContext());
-        return priceOverride.getOrCreateOverriddenPlan(defaultPlan, new DateTime(getEffectiveDate()), overrides.getOverrides(), internalCallContext);
+        return priceOverride.getOrCreateOverriddenPlan(defaultPlan, internalCallContext.toUTCDateTime(getEffectiveDate()), overrides.getOverrides(), internalCallContext);
     }
 
     @Override
     public Plan findCurrentPlan(final String planName) throws CatalogApiException {
-
         final Matcher m = DefaultPriceOverride.CUSTOM_PLAN_NAME_PATTERN.matcher(planName);
         if (m.matches()) {
-            final InternalTenantContext internalTenantContext = internalCallContextFactory.createInternalTenantContext(tenantRecordId, null);
+            final InternalTenantContext internalTenantContext = createInternalTenantContext();
             return priceOverride.getOverriddenPlan(planName, standaloneCatalog, internalTenantContext);
         }
         return standaloneCatalog.findCurrentPlan(planName);
@@ -165,8 +163,8 @@ public class StandaloneCatalogWithPriceOverride extends ValidatingConfig<Standal
         final String planName = DefaultPlanPhase.planName(phaseName);
         final Matcher m = DefaultPriceOverride.CUSTOM_PLAN_NAME_PATTERN.matcher(planName);
         if (m.matches()) {
-            final InternalTenantContext internalTenantContext = internalCallContextFactory.createInternalTenantContext(tenantRecordId, null);
-            Plan plan = priceOverride.getOverriddenPlan(planName, standaloneCatalog, internalTenantContext);
+            final InternalTenantContext internalTenantContext = createInternalTenantContext();
+            final Plan plan = priceOverride.getOverriddenPlan(planName, standaloneCatalog, internalTenantContext);
             return plan.findPhase(phaseName);
         }
         return standaloneCatalog.findCurrentPhase(phaseName);
@@ -241,4 +239,7 @@ public class StandaloneCatalogWithPriceOverride extends ValidatingConfig<Standal
         return standaloneCatalog.findCurrentPriceList(priceListName);
     }
 
+    private InternalTenantContext createInternalTenantContext() {
+        return internalCallContextFactory.createInternalTenantContext(tenantRecordId, null);
+    }
 }
diff --git a/catalog/src/main/java/org/killbill/billing/catalog/VersionedCatalog.java b/catalog/src/main/java/org/killbill/billing/catalog/VersionedCatalog.java
index b0aa352..dbe3b2b 100644
--- a/catalog/src/main/java/org/killbill/billing/catalog/VersionedCatalog.java
+++ b/catalog/src/main/java/org/killbill/billing/catalog/VersionedCatalog.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
@@ -65,21 +65,23 @@ import org.killbill.xmlloader.ValidationErrors;
 public class VersionedCatalog extends ValidatingConfig<StandaloneCatalogWithPriceOverride> implements Catalog, StaticCatalog {
 
     private final Clock clock;
+    private final InternalTenantContext internalTenantContext;
+    @XmlElement(name = "catalogVersion", required = true)
+    private final List<StandaloneCatalogWithPriceOverride> versions;
     private String catalogName;
     private BillingMode recurringBillingMode;
 
-    @XmlElement(name = "catalogVersion", required = true)
-    private List<StandaloneCatalogWithPriceOverride> versions;
-
     // Required for JAXB deserialization
     public VersionedCatalog() {
         this.clock = null;
-        versions = new ArrayList<StandaloneCatalogWithPriceOverride>();
+        this.versions = new ArrayList<StandaloneCatalogWithPriceOverride>();
+        this.internalTenantContext = new InternalTenantContext(null, null);
     }
 
     public VersionedCatalog(final Clock clock) {
         this.clock = clock;
-        versions = new ArrayList<StandaloneCatalogWithPriceOverride>();
+        this.versions = new ArrayList<StandaloneCatalogWithPriceOverride>();
+        this.internalTenantContext = new InternalTenantContext(null, null);
     }
 
     public VersionedCatalog(final Clock clock, final String catalogName, final BillingMode recurringBillingMode, final List<StandaloneCatalogWithPriceOverride> versions, final InternalTenantContext tenantContext) {
@@ -91,9 +93,9 @@ public class VersionedCatalog extends ValidatingConfig<StandaloneCatalogWithPric
             final StandaloneCatalogWithPriceOverride catalogWithTenantInfo = new StandaloneCatalogWithPriceOverride(cur, tenantContext);
             this.versions.add(catalogWithTenantInfo);
         }
+        this.internalTenantContext = tenantContext;
     }
 
-
     //
     // Private methods
     //
@@ -154,16 +156,16 @@ public class VersionedCatalog extends ValidatingConfig<StandaloneCatalogWithPric
                           final DateTime subscriptionStartDate)
             throws CatalogApiException {
         final List<StandaloneCatalogWithPriceOverride> catalogs = versionsBeforeDate(requestedDate.toDate());
-        if (catalogs.size() == 0) {
+        if (catalogs.isEmpty()) {
             throw new CatalogApiException(ErrorCode.CAT_NO_CATALOG_FOR_GIVEN_DATE, requestedDate.toDate().toString());
         }
 
         for (int i = catalogs.size() - 1; i >= 0; i--) { // Working backwards to find the latest applicable plan
             final StandaloneCatalogWithPriceOverride c = catalogs.get(i);
-            Plan plan;
+            final Plan plan;
             try {
                 plan = wrapper.findPlan(c);
-            } catch (CatalogApiException e) {
+            } catch (final CatalogApiException e) {
                 if (e.getCode() != ErrorCode.CAT_NO_SUCH_PLAN.getCode()) {
                     throw e;
                 } else {
@@ -172,12 +174,12 @@ public class VersionedCatalog extends ValidatingConfig<StandaloneCatalogWithPric
                 }
             }
 
-            DateTime catalogEffectiveDate = new DateTime(c.getEffectiveDate());
+            final DateTime catalogEffectiveDate = internalTenantContext.toUTCDateTime(c.getEffectiveDate());
             if (!subscriptionStartDate.isBefore(catalogEffectiveDate)) { // Its a new subscription this plan always applies
                 return plan;
             } else { //Its an existing subscription
                 if (plan.getEffectiveDateForExistingSubscriptons() != null) { //if it is null any change to this does not apply to existing subscriptions
-                    final DateTime existingSubscriptionDate = new DateTime(plan.getEffectiveDateForExistingSubscriptons());
+                    final DateTime existingSubscriptionDate = internalTenantContext.toUTCDateTime(plan.getEffectiveDateForExistingSubscriptons());
                     if (requestedDate.isAfter(existingSubscriptionDate)) { // this plan is now applicable to existing subs
                         return plan;
                     }
@@ -271,10 +273,10 @@ public class VersionedCatalog extends ValidatingConfig<StandaloneCatalogWithPric
 
     @Override
     public Plan createOrFindPlan(final String productName,
-                         final BillingPeriod term,
-                         final String priceListName,
-                         final PlanPhasePriceOverridesWithCallContext overrides,
-                         final DateTime requestedDate)
+                                 final BillingPeriod term,
+                                 final String priceListName,
+                                 final PlanPhasePriceOverridesWithCallContext overrides,
+                                 final DateTime requestedDate)
             throws CatalogApiException {
         return versionForDate(requestedDate).createOrFindCurrentPlan(productName, term, priceListName, overrides);
     }
@@ -289,11 +291,11 @@ public class VersionedCatalog extends ValidatingConfig<StandaloneCatalogWithPric
 
     @Override
     public Plan createOrFindPlan(final String productName,
-                         final BillingPeriod term,
-                         final String priceListName,
-                         final PlanPhasePriceOverridesWithCallContext overrides,
-                         final DateTime requestedDate,
-                         final DateTime subscriptionStartDate)
+                                 final BillingPeriod term,
+                                 final String priceListName,
+                                 final PlanPhasePriceOverridesWithCallContext overrides,
+                                 final DateTime requestedDate,
+                                 final DateTime subscriptionStartDate)
             throws CatalogApiException {
         return findPlan(new PlanRequestWrapper(productName, term, priceListName, overrides), requestedDate, subscriptionStartDate);
     }
@@ -434,7 +436,7 @@ public class VersionedCatalog extends ValidatingConfig<StandaloneCatalogWithPric
 
     @Override
     public Plan createOrFindCurrentPlan(final String productName, final BillingPeriod term,
-                                final String priceList, PlanPhasePriceOverridesWithCallContext overrides) throws CatalogApiException {
+                                        final String priceList, final PlanPhasePriceOverridesWithCallContext overrides) throws CatalogApiException {
         return versionForDate(clock.getUTCNow()).createOrFindCurrentPlan(productName, term, priceList, overrides);
     }
 
@@ -512,7 +514,7 @@ public class VersionedCatalog extends ValidatingConfig<StandaloneCatalogWithPric
     }
 
     @Override
-    public boolean compliesWithLimits(String phaseName, String unit, double value) throws CatalogApiException {
+    public boolean compliesWithLimits(final String phaseName, final String unit, final double value) throws CatalogApiException {
         return versionForDate(clock.getUTCNow()).compliesWithLimits(phaseName, unit, value);
     }
 }

currency/pom.xml 2(+1 -1)

diff --git a/currency/pom.xml b/currency/pom.xml
index dfec699..049a773 100644
--- a/currency/pom.xml
+++ b/currency/pom.xml
@@ -19,7 +19,7 @@
     <parent>
         <artifactId>killbill</artifactId>
         <groupId>org.kill-bill.billing</groupId>
-        <version>0.16.1-SNAPSHOT</version>
+        <version>0.16.2-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>killbill-currency</artifactId>
diff --git a/entitlement/pom.xml b/entitlement/pom.xml
index c66ead0..83f81aa 100644
--- a/entitlement/pom.xml
+++ b/entitlement/pom.xml
@@ -19,7 +19,7 @@
     <parent>
         <artifactId>killbill</artifactId>
         <groupId>org.kill-bill.billing</groupId>
-        <version>0.16.1-SNAPSHOT</version>
+        <version>0.16.2-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>killbill-entitlement</artifactId>
diff --git a/entitlement/src/main/java/org/killbill/billing/entitlement/api/BlockingStateOrdering.java b/entitlement/src/main/java/org/killbill/billing/entitlement/api/BlockingStateOrdering.java
index 065076f..617db77 100644
--- a/entitlement/src/main/java/org/killbill/billing/entitlement/api/BlockingStateOrdering.java
+++ b/entitlement/src/main/java/org/killbill/billing/entitlement/api/BlockingStateOrdering.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
@@ -31,6 +31,7 @@ import javax.annotation.Nullable;
 
 import org.joda.time.DateTime;
 import org.joda.time.DateTimeZone;
+import org.killbill.billing.callcontext.InternalTenantContext;
 import org.killbill.billing.catalog.api.BillingPeriod;
 import org.killbill.billing.catalog.api.Plan;
 import org.killbill.billing.catalog.api.PlanPhase;
@@ -41,10 +42,8 @@ import org.killbill.billing.entitlement.block.BlockingChecker.BlockingAggregator
 import org.killbill.billing.entitlement.block.DefaultBlockingChecker.DefaultBlockingAggregator;
 import org.killbill.billing.junction.DefaultBlockingState;
 
-import com.google.common.base.Function;
 import com.google.common.base.Preconditions;
 import com.google.common.collect.ImmutableList;
-import com.google.common.collect.Iterables;
 
 // Given an event stream (across one or multiple entitlements), insert the blocking events at the right place
 public class BlockingStateOrdering extends EntitlementOrderingBase {
@@ -53,11 +52,11 @@ public class BlockingStateOrdering extends EntitlementOrderingBase {
 
     private BlockingStateOrdering() {}
 
-    public static void insertSorted(final Iterable<Entitlement> entitlements, final DateTimeZone accountTimeZone, final LinkedList<SubscriptionEvent> result) {
-        INSTANCE.computeEvents(entitlements, accountTimeZone, result);
+    public static void insertSorted(final Iterable<Entitlement> entitlements, final DateTimeZone accountTimeZone, final InternalTenantContext internalTenantContext, final LinkedList<SubscriptionEvent> result) {
+        INSTANCE.computeEvents(entitlements, accountTimeZone, internalTenantContext, result);
     }
 
-    private void computeEvents(final Iterable<Entitlement> entitlements, final DateTimeZone accountTimeZone, final LinkedList<SubscriptionEvent> result) {
+    private void computeEvents(final Iterable<Entitlement> entitlements, final DateTimeZone accountTimeZone, final InternalTenantContext internalTenantContext, final LinkedList<SubscriptionEvent> result) {
         final Collection<UUID> allEntitlementUUIDs = new HashSet<UUID>();
         final Collection<BlockingState> blockingStates = new LinkedList<BlockingState>();
         for (final Entitlement entitlement : entitlements) {
@@ -69,13 +68,13 @@ public class BlockingStateOrdering extends EntitlementOrderingBase {
         // Trust the incoming ordering here: blocking states were sorted using ProxyBlockingStateDao#sortedCopy
         for (final BlockingState bs : blockingStates) {
             final List<SubscriptionEvent> newEvents = new ArrayList<SubscriptionEvent>();
-            final int index = insertFromBlockingEvent(accountTimeZone, allEntitlementUUIDs, result, bs, bs.getEffectiveDate(), newEvents);
+            final int index = insertFromBlockingEvent(accountTimeZone, internalTenantContext, allEntitlementUUIDs, result, bs, bs.getEffectiveDate(), newEvents);
             insertAfterIndex(result, newEvents, index);
         }
     }
 
     // Returns the index and the newEvents generated from the incoming blocking state event. Those new events will all be created for the same effectiveDate and should be ordered.
-    private int insertFromBlockingEvent(final DateTimeZone accountTimeZone, final Collection<UUID> allEntitlementUUIDs, final List<SubscriptionEvent> result, final BlockingState bs, final DateTime bsEffectiveDate, final List<SubscriptionEvent> newEvents) {
+    private int insertFromBlockingEvent(final DateTimeZone accountTimeZone, final InternalTenantContext internalTenantContext, final Collection<UUID> allEntitlementUUIDs, final List<SubscriptionEvent> result, final BlockingState bs, final DateTime bsEffectiveDate, final Collection<SubscriptionEvent> newEvents) {
         // Keep the current state per entitlement
         final Map<UUID, TargetState> targetStates = new HashMap<UUID, TargetState>();
         for (final UUID cur : allEntitlementUUIDs) {
@@ -134,7 +133,7 @@ public class BlockingStateOrdering extends EntitlementOrderingBase {
 
             final List<SubscriptionEventType> eventTypes = curTargetState.addStateAndReturnEventTypes(bs);
             for (final SubscriptionEventType t : eventTypes) {
-                newEvents.add(toSubscriptionEvent(prevNext[0], prevNext[1], targetEntitlementId, bs, t, accountTimeZone));
+                newEvents.add(toSubscriptionEvent(prevNext[0], prevNext[1], targetEntitlementId, bs, t, accountTimeZone, internalTenantContext));
             }
         }
         return index;
@@ -176,7 +175,8 @@ public class BlockingStateOrdering extends EntitlementOrderingBase {
     }
 
     private SubscriptionEvent toSubscriptionEvent(@Nullable final SubscriptionEvent prev, @Nullable final SubscriptionEvent next,
-                                                  final UUID entitlementId, final BlockingState in, final SubscriptionEventType eventType, final DateTimeZone accountTimeZone) {
+                                                  final UUID entitlementId, final BlockingState in, final SubscriptionEventType eventType,
+                                                  final DateTimeZone accountTimeZone, final InternalTenantContext internalTenantContext) {
         final Product prevProduct;
         final Plan prevPlan;
         final PlanPhase prevPlanPhase;
@@ -257,7 +257,8 @@ public class BlockingStateOrdering extends EntitlementOrderingBase {
                                             nextPriceList,
                                             nextBillingPeriod,
                                             in.getCreatedDate(),
-                                            accountTimeZone);
+                                            accountTimeZone,
+                                            internalTenantContext);
     }
 
     private void insertAfterIndex(final LinkedList<SubscriptionEvent> original, final Collection<SubscriptionEvent> newEvents, final int index) {
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 ca69a1f..b0f2135 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
@@ -30,6 +30,7 @@ import org.joda.time.DateTimeZone;
 import org.joda.time.LocalDate;
 import org.killbill.billing.ErrorCode;
 import org.killbill.billing.callcontext.InternalCallContext;
+import org.killbill.billing.callcontext.InternalTenantContext;
 import org.killbill.billing.catalog.api.BillingActionPolicy;
 import org.killbill.billing.catalog.api.BillingPeriod;
 import org.killbill.billing.catalog.api.Plan;
@@ -76,6 +77,7 @@ public class DefaultEntitlement extends EntityBase implements Entitlement {
     private final SecurityApi securityApi;
     protected final EventsStreamBuilder eventsStreamBuilder;
     protected final EntitlementDateHelper dateHelper;
+    protected final InternalTenantContext internalTenantContext;
     protected final InternalCallContextFactory internalCallContextFactory;
     protected final Clock clock;
     protected final BlockingChecker checker;
@@ -98,14 +100,14 @@ public class DefaultEntitlement extends EntityBase implements Entitlement {
                               final InternalCallContextFactory internalCallContextFactory, final TenantContext tenantContext) throws EntitlementApiException {
         this(eventsStreamBuilder.buildForEntitlement(entitlementId, tenantContext), eventsStreamBuilder,
              entitlementApi, pluginExecution, blockingStateDao, subscriptionInternalApi, checker, notificationQueueService,
-             entitlementUtils, dateHelper, clock, securityApi, internalCallContextFactory);
+             entitlementUtils, dateHelper, clock, securityApi, internalCallContextFactory.createInternalTenantContext(tenantContext), internalCallContextFactory);
     }
 
     public DefaultEntitlement(final EventsStream eventsStream, final EventsStreamBuilder eventsStreamBuilder,
                               final EntitlementApi entitlementApi, final EntitlementPluginExecution pluginExecution, final BlockingStateDao blockingStateDao,
                               final SubscriptionBaseInternalApi subscriptionInternalApi, final BlockingChecker checker,
                               final NotificationQueueService notificationQueueService, final EntitlementUtils entitlementUtils,
-                              final EntitlementDateHelper dateHelper, final Clock clock, final SecurityApi securityApi, final InternalCallContextFactory internalCallContextFactory) {
+                              final EntitlementDateHelper dateHelper, final Clock clock, final SecurityApi securityApi, final InternalTenantContext internalTenantContext, final InternalCallContextFactory internalCallContextFactory) {
         super(eventsStream.getEntitlementId(), eventsStream.getSubscriptionBase().getCreatedDate(), eventsStream.getSubscriptionBase().getUpdatedDate());
         this.eventsStreamBuilder = eventsStreamBuilder;
         this.eventsStream = eventsStream;
@@ -113,6 +115,7 @@ public class DefaultEntitlement extends EntityBase implements Entitlement {
         this.entitlementApi = entitlementApi;
         this.pluginExecution = pluginExecution;
         this.subscriptionInternalApi = subscriptionInternalApi;
+        this.internalTenantContext = internalTenantContext;
         this.internalCallContextFactory = internalCallContextFactory;
         this.clock = clock;
         this.securityApi = securityApi;
@@ -135,6 +138,7 @@ public class DefaultEntitlement extends EntityBase implements Entitlement {
              in.getDateHelper(),
              in.getClock(),
              in.getSecurityApi(),
+             in.getInternalTenantContext(),
              in.getInternalCallContextFactory());
     }
 
@@ -164,6 +168,10 @@ public class DefaultEntitlement extends EntityBase implements Entitlement {
         return dateHelper;
     }
 
+    public InternalTenantContext getInternalTenantContext() {
+        return internalTenantContext;
+    }
+
     public InternalCallContextFactory getInternalCallContextFactory() {
         return internalCallContextFactory;
     }
@@ -236,7 +244,7 @@ public class DefaultEntitlement extends EntityBase implements Entitlement {
 
     @Override
     public LocalDate getEffectiveStartDate() {
-        return new LocalDate(getSubscriptionBase().getStartDate(), eventsStream.getAccountTimeZone());
+        return internalTenantContext.toLocalDate(getSubscriptionBase().getStartDate(), eventsStream.getAccountTimeZone());
     }
 
     @Override
@@ -438,8 +446,7 @@ public class DefaultEntitlement extends EntityBase implements Entitlement {
                 // (we don't want an entitlement cancel date one second or so after the subscription cancel date or add-ons cancellations
                 // computations won't work).
                 final InternalCallContext contextWithValidAccountRecordId = internalCallContextFactory.createInternalCallContext(getAccountId(), callContext);
-                final LocalDate effectiveLocalDate = new LocalDate(updatedPluginContext.getEffectiveDate(), eventsStream.getAccountTimeZone());
-                final DateTime effectiveDate = dateHelper.fromLocalDateAndReferenceTime(effectiveLocalDate, getSubscriptionBase().getStartDate(), contextWithValidAccountRecordId);
+                final DateTime effectiveDate = dateHelper.fromLocalDateAndReferenceTime(updatedPluginContext.getEffectiveDate(), getSubscriptionBase().getStartDate(), contextWithValidAccountRecordId);
 
                 try {
                     // Cancel subscription base first, to correctly compute the add-ons entitlements we need to cancel (see below)
@@ -468,10 +475,14 @@ public class DefaultEntitlement extends EntityBase implements Entitlement {
         final LocalDate cancellationDate;
         switch (entitlementPolicy) {
             case IMMEDIATE:
-                cancellationDate = new LocalDate(clock.getUTCNow(), eventsStream.getAccountTimeZone());
+                cancellationDate = clock.getToday(eventsStream.getAccountTimeZone());
                 break;
             case END_OF_TERM:
-                cancellationDate = getSubscriptionBase().getChargedThroughDate() != null ? new LocalDate(getSubscriptionBase().getChargedThroughDate(), eventsStream.getAccountTimeZone()) : new LocalDate(clock.getUTCNow(), eventsStream.getAccountTimeZone());
+                if (getSubscriptionBase().getChargedThroughDate() != null) {
+                    cancellationDate = internalTenantContext.toLocalDate(getSubscriptionBase().getChargedThroughDate(), eventsStream.getAccountTimeZone());
+                } else {
+                    cancellationDate = clock.getToday(eventsStream.getAccountTimeZone());
+                }
                 break;
             default:
                 throw new RuntimeException("Unsupported policy " + entitlementPolicy);
diff --git a/entitlement/src/main/java/org/killbill/billing/entitlement/api/DefaultEntitlementApi.java b/entitlement/src/main/java/org/killbill/billing/entitlement/api/DefaultEntitlementApi.java
index 0560b0e..6e6cd45 100644
--- a/entitlement/src/main/java/org/killbill/billing/entitlement/api/DefaultEntitlementApi.java
+++ b/entitlement/src/main/java/org/killbill/billing/entitlement/api/DefaultEntitlementApi.java
@@ -135,7 +135,6 @@ public class DefaultEntitlementApi extends DefaultEntitlementApiBase implements 
             public Entitlement doCall(final EntitlementApi entitlementApi, final EntitlementContext updatedPluginContext) throws EntitlementApiException {
                 final InternalCallContext contextWithValidAccountRecordId = internalCallContextFactory.createInternalCallContext(accountId, callContext);
                 try {
-
                     if (entitlementUtils.getFirstActiveSubscriptionIdForKeyOrNull(externalKey, contextWithValidAccountRecordId) != null) {
                         throw new EntitlementApiException(new SubscriptionBaseApiException(ErrorCode.SUB_CREATE_ACTIVE_BUNDLE_KEY_EXISTS, externalKey));
                     }
@@ -337,7 +336,7 @@ public class DefaultEntitlementApi extends DefaultEntitlementApiBase implements 
                                                               public Entitlement apply(final EventsStream eventsStream) {
                                                                   return new DefaultEntitlement(eventsStream, eventsStreamBuilder, entitlementApi, pluginExecution,
                                                                                                 blockingStateDao, subscriptionBaseInternalApi, checker, notificationQueueService,
-                                                                                                entitlementUtils, dateHelper, clock, securityApi, internalCallContextFactory);
+                                                                                                entitlementUtils, dateHelper, clock, securityApi, context, internalCallContextFactory);
                                                               }
                                                           });
     }
diff --git a/entitlement/src/main/java/org/killbill/billing/entitlement/api/DefaultSubscription.java b/entitlement/src/main/java/org/killbill/billing/entitlement/api/DefaultSubscription.java
index 3667835..8a953e2 100644
--- a/entitlement/src/main/java/org/killbill/billing/entitlement/api/DefaultSubscription.java
+++ b/entitlement/src/main/java/org/killbill/billing/entitlement/api/DefaultSubscription.java
@@ -1,7 +1,7 @@
 /*
  * Copyright 2010-2013 Ning, Inc.
- * 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
@@ -31,7 +31,7 @@ public class DefaultSubscription extends DefaultEntitlement implements Subscript
 
     @Override
     public LocalDate getBillingStartDate() {
-        return new LocalDate(getSubscriptionBase().getStartDate(), getAccountTimeZone());
+        return internalTenantContext.toLocalDate(getSubscriptionBase().getStartDate(), getAccountTimeZone());
     }
 
     @Override
@@ -51,16 +51,16 @@ public class DefaultSubscription extends DefaultEntitlement implements Subscript
             futureOrCurrentEndDate = futureOrCurrentEndDateForSubscription;
         }
 
-        return futureOrCurrentEndDate != null ? new LocalDate(futureOrCurrentEndDate, getAccountTimeZone()) : null;
+        return futureOrCurrentEndDate != null ? internalTenantContext.toLocalDate(futureOrCurrentEndDate, getAccountTimeZone()) : null;
     }
 
     @Override
     public LocalDate getChargedThroughDate() {
-        return getSubscriptionBase().getChargedThroughDate() != null ? new LocalDate(getSubscriptionBase().getChargedThroughDate(), getAccountTimeZone()) : null;
+        return getSubscriptionBase().getChargedThroughDate() != null ? internalTenantContext.toLocalDate(getSubscriptionBase().getChargedThroughDate(), getAccountTimeZone()) : null;
     }
 
     @Override
     public List<SubscriptionEvent> getSubscriptionEvents() {
-        return SubscriptionEventOrdering.sortedCopy(this, getAccountTimeZone());
+        return SubscriptionEventOrdering.sortedCopy(this, getAccountTimeZone(), internalTenantContext);
     }
 }
diff --git a/entitlement/src/main/java/org/killbill/billing/entitlement/api/DefaultSubscriptionApi.java b/entitlement/src/main/java/org/killbill/billing/entitlement/api/DefaultSubscriptionApi.java
index 17f87de..c9b180b 100644
--- a/entitlement/src/main/java/org/killbill/billing/entitlement/api/DefaultSubscriptionApi.java
+++ b/entitlement/src/main/java/org/killbill/billing/entitlement/api/DefaultSubscriptionApi.java
@@ -1,7 +1,7 @@
 /*
  * Copyright 2010-2013 Ning, Inc.
- * 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
@@ -431,10 +431,11 @@ public class DefaultSubscriptionApi implements SubscriptionApi {
     }
 
     private List<SubscriptionBundle> getSubscriptionBundlesForAccount(final UUID accountId, final TenantContext tenantContext) throws SubscriptionApiException {
+        final InternalTenantContext internalTenantContextWithValidAccountRecordId = internalCallContextFactory.createInternalTenantContext(accountId, tenantContext);
+
         // Retrieve entitlements
         final AccountEntitlements accountEntitlements;
         try {
-            final InternalTenantContext internalTenantContextWithValidAccountRecordId = internalCallContextFactory.createInternalTenantContext(accountId, tenantContext);
             accountEntitlements = entitlementInternalApi.getAllEntitlementsForAccountId(accountId, internalTenantContextWithValidAccountRecordId);
         } catch (final EntitlementApiException e) {
             throw new SubscriptionApiException(e);
@@ -455,7 +456,8 @@ public class DefaultSubscriptionApi implements SubscriptionApi {
                                                                                               accountId,
                                                                                               bundleId,
                                                                                               externalKey,
-                                                                                              accountEntitlements.getEntitlements().get(bundleId));
+                                                                                              accountEntitlements.getEntitlements().get(bundleId),
+                                                                                              internalTenantContextWithValidAccountRecordId);
 
             final SubscriptionBaseBundle baseBundle = accountEntitlements.getBundles().get(bundleId);
             final SubscriptionBundle subscriptionBundle = new DefaultSubscriptionBundle(bundleId,
diff --git a/entitlement/src/main/java/org/killbill/billing/entitlement/api/DefaultSubscriptionBundleTimeline.java b/entitlement/src/main/java/org/killbill/billing/entitlement/api/DefaultSubscriptionBundleTimeline.java
index 91e5758..4ae583f 100644
--- a/entitlement/src/main/java/org/killbill/billing/entitlement/api/DefaultSubscriptionBundleTimeline.java
+++ b/entitlement/src/main/java/org/killbill/billing/entitlement/api/DefaultSubscriptionBundleTimeline.java
@@ -1,7 +1,7 @@
 /*
  * Copyright 2010-2013 Ning, Inc.
- * 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
@@ -22,6 +22,7 @@ import java.util.List;
 import java.util.UUID;
 
 import org.joda.time.DateTimeZone;
+import org.killbill.billing.callcontext.InternalTenantContext;
 
 public class DefaultSubscriptionBundleTimeline implements SubscriptionBundleTimeline {
 
@@ -30,11 +31,16 @@ public class DefaultSubscriptionBundleTimeline implements SubscriptionBundleTime
     private final String externalKey;
     private final List<SubscriptionEvent> events;
 
-    public DefaultSubscriptionBundleTimeline(final DateTimeZone accountTimeZone, final UUID accountId, final UUID bundleId, final String externalKey, final Iterable<Entitlement> entitlements) {
+    public DefaultSubscriptionBundleTimeline(final DateTimeZone accountTimeZone,
+                                             final UUID accountId,
+                                             final UUID bundleId,
+                                             final String externalKey,
+                                             final Iterable<Entitlement> entitlements,
+                                             final InternalTenantContext internalTenantContext) {
         this.accountId = accountId;
         this.bundleId = bundleId;
         this.externalKey = externalKey;
-        this.events = SubscriptionEventOrdering.sortedCopy(entitlements, accountTimeZone);
+        this.events = SubscriptionEventOrdering.sortedCopy(entitlements, accountTimeZone, internalTenantContext);
     }
 
     @Override
diff --git a/entitlement/src/main/java/org/killbill/billing/entitlement/api/DefaultSubscriptionEvent.java b/entitlement/src/main/java/org/killbill/billing/entitlement/api/DefaultSubscriptionEvent.java
index 78af96f..b4e630a 100644
--- a/entitlement/src/main/java/org/killbill/billing/entitlement/api/DefaultSubscriptionEvent.java
+++ b/entitlement/src/main/java/org/killbill/billing/entitlement/api/DefaultSubscriptionEvent.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:
  *
@@ -21,7 +23,7 @@ import java.util.UUID;
 import org.joda.time.DateTime;
 import org.joda.time.DateTimeZone;
 import org.joda.time.LocalDate;
-
+import org.killbill.billing.callcontext.InternalTenantContext;
 import org.killbill.billing.catalog.api.BillingPeriod;
 import org.killbill.billing.catalog.api.Plan;
 import org.killbill.billing.catalog.api.PlanPhase;
@@ -51,6 +53,7 @@ public class DefaultSubscriptionEvent implements SubscriptionEvent {
     private final BillingPeriod nextBillingPeriod;
     private final DateTime createdDate;
     private final DateTimeZone accountTimeZone;
+    private final InternalTenantContext internalTenantContext;
 
     public DefaultSubscriptionEvent(final UUID id,
                                     final UUID entitlementId,
@@ -71,7 +74,8 @@ public class DefaultSubscriptionEvent implements SubscriptionEvent {
                                     final PriceList nextPriceList,
                                     final BillingPeriod nextBillingPeriod,
                                     final DateTime createDate,
-                                    final DateTimeZone accountTimeZone) {
+                                    final DateTimeZone accountTimeZone,
+                                    final InternalTenantContext internalTenantContext) {
         this.id = id;
         this.entitlementId = entitlementId;
         this.effectiveDate = effectiveDate;
@@ -93,6 +97,7 @@ public class DefaultSubscriptionEvent implements SubscriptionEvent {
         this.nextBillingPeriod = nextBillingPeriod;
         this.createdDate = createDate;
         this.accountTimeZone = accountTimeZone;
+        this.internalTenantContext = internalTenantContext;
     }
 
     public DateTimeZone getAccountTimeZone() {
@@ -119,10 +124,9 @@ public class DefaultSubscriptionEvent implements SubscriptionEvent {
 
     @Override
     public LocalDate getEffectiveDate() {
-        return effectiveDate != null ? new LocalDate(effectiveDate, accountTimeZone) : null;
+        return effectiveDate != null ? internalTenantContext.toLocalDate(effectiveDate, accountTimeZone) : null;
     }
 
-    @Override
     public SubscriptionEventType getSubscriptionEventType() {
         return eventType;
     }
diff --git a/entitlement/src/main/java/org/killbill/billing/entitlement/api/EntitlementDateHelper.java b/entitlement/src/main/java/org/killbill/billing/entitlement/api/EntitlementDateHelper.java
index d802daa..1378573 100644
--- a/entitlement/src/main/java/org/killbill/billing/entitlement/api/EntitlementDateHelper.java
+++ b/entitlement/src/main/java/org/killbill/billing/entitlement/api/EntitlementDateHelper.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:
  *
@@ -39,8 +41,8 @@ public class EntitlementDateHelper {
     public DateTime fromLocalDateAndReferenceTime(final LocalDate requestedDate, final DateTime referenceDateTime, final InternalTenantContext callContext) throws EntitlementApiException {
         try {
             final ImmutableAccountData account = accountApi.getImmutableAccountDataByRecordId(callContext.getAccountRecordId(), callContext);
-            return ClockUtil.computeDateTimeWithUTCReferenceTime(requestedDate, referenceDateTime.toDateTime(DateTimeZone.UTC).toLocalTime(), account.getTimeZone(), clock);
-        } catch (AccountApiException e) {
+            return ClockUtil.computeDateTimeWithUTCReferenceTime(requestedDate, callContext.toUTCDateTime(referenceDateTime).toLocalTime(), account.getTimeZone(), clock);
+        } catch (final AccountApiException e) {
             throw new EntitlementApiException(e);
         }
     }
@@ -50,13 +52,13 @@ public class EntitlementDateHelper {
      *
      * @param inputDate       the fully qualified DateTime
      * @param accountTimeZone the account timezone
+     * @param internalTenantContext the context
      * @return true if the inputDate, once converted into a LocalDate using account timezone is less or equals than today
      */
     // TODO Move to ClockUtils
-    public boolean isBeforeOrEqualsToday(final DateTime inputDate, final DateTimeZone accountTimeZone) {
-        final LocalDate localDateNowInAccountTimezone = new LocalDate(clock.getUTCNow(), accountTimeZone);
-        final LocalDate targetDateInAccountTimezone = new LocalDate(inputDate, accountTimeZone);
+    public boolean isBeforeOrEqualsToday(final DateTime inputDate, final DateTimeZone accountTimeZone, final InternalTenantContext internalTenantContext) {
+        final LocalDate localDateNowInAccountTimezone = clock.getToday(accountTimeZone);
+        final LocalDate targetDateInAccountTimezone = internalTenantContext.toLocalDate(inputDate, accountTimeZone);
         return targetDateInAccountTimezone.compareTo(localDateNowInAccountTimezone) <= 0;
     }
-
 }
diff --git a/entitlement/src/main/java/org/killbill/billing/entitlement/api/SubscriptionEventOrdering.java b/entitlement/src/main/java/org/killbill/billing/entitlement/api/SubscriptionEventOrdering.java
index 26ae84e..01bff1d 100644
--- a/entitlement/src/main/java/org/killbill/billing/entitlement/api/SubscriptionEventOrdering.java
+++ b/entitlement/src/main/java/org/killbill/billing/entitlement/api/SubscriptionEventOrdering.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
@@ -25,6 +25,7 @@ import java.util.List;
 import java.util.Map;
 
 import org.joda.time.DateTimeZone;
+import org.killbill.billing.callcontext.InternalTenantContext;
 import org.killbill.billing.entitlement.DefaultEntitlementService;
 import org.killbill.billing.subscription.api.SubscriptionBase;
 import org.killbill.billing.subscription.api.SubscriptionBaseTransitionType;
@@ -47,20 +48,20 @@ public class SubscriptionEventOrdering extends EntitlementOrderingBase {
 
     private SubscriptionEventOrdering() {}
 
-    public static List<SubscriptionEvent> sortedCopy(final Entitlement entitlement, final DateTimeZone accountTimeZone) {
-        return sortedCopy(ImmutableList.<Entitlement>of(entitlement), accountTimeZone);
+    public static List<SubscriptionEvent> sortedCopy(final Entitlement entitlement, final DateTimeZone accountTimeZone, final InternalTenantContext internalTenantContext) {
+        return sortedCopy(ImmutableList.<Entitlement>of(entitlement), accountTimeZone, internalTenantContext);
     }
 
-    public static List<SubscriptionEvent> sortedCopy(final Iterable<Entitlement> entitlements, final DateTimeZone accountTimeZone) {
-        return INSTANCE.computeEvents(entitlements, accountTimeZone);
+    public static List<SubscriptionEvent> sortedCopy(final Iterable<Entitlement> entitlements, final DateTimeZone accountTimeZone, final InternalTenantContext internalTenantContext) {
+        return INSTANCE.computeEvents(entitlements, accountTimeZone, internalTenantContext);
     }
 
-    private List<SubscriptionEvent> computeEvents(final Iterable<Entitlement> entitlements, final DateTimeZone accountTimeZone) {
+    private List<SubscriptionEvent> computeEvents(final Iterable<Entitlement> entitlements, final DateTimeZone accountTimeZone, final InternalTenantContext internalTenantContext) {
         // Compute base events across all entitlements (already ordered per entitlement)
-        final LinkedList<SubscriptionEvent> result = computeSubscriptionBaseEvents(entitlements, accountTimeZone);
+        final LinkedList<SubscriptionEvent> result = computeSubscriptionBaseEvents(entitlements, accountTimeZone, internalTenantContext);
 
         // Add blocking states at the right place
-        BlockingStateOrdering.insertSorted(entitlements, accountTimeZone, result);
+        BlockingStateOrdering.insertSorted(entitlements, accountTimeZone, internalTenantContext, result);
 
         // Final cleanups
         reOrderSubscriptionEventsOnSameDateByType(result);
@@ -70,7 +71,7 @@ public class SubscriptionEventOrdering extends EntitlementOrderingBase {
     }
 
     // Compute the initial stream of events based on the subscription base events
-    private LinkedList<SubscriptionEvent> computeSubscriptionBaseEvents(final Iterable<Entitlement> entitlements, final DateTimeZone accountTimeZone) {
+    private LinkedList<SubscriptionEvent> computeSubscriptionBaseEvents(final Iterable<Entitlement> entitlements, final DateTimeZone accountTimeZone, final InternalTenantContext internalTenantContext) {
         final LinkedList<SubscriptionEvent> result = new LinkedList<SubscriptionEvent>();
         for (final Entitlement cur : entitlements) {
             Preconditions.checkState(cur instanceof DefaultEntitlement, "Entitlement %s is not a DefaultEntitlement", cur);
@@ -79,7 +80,7 @@ public class SubscriptionEventOrdering extends EntitlementOrderingBase {
             for (final SubscriptionBaseTransition tr : baseTransitions) {
                 final List<SubscriptionEventType> eventTypes = toEventTypes(tr.getTransitionType());
                 for (final SubscriptionEventType eventType : eventTypes) {
-                    final SubscriptionEvent event = toSubscriptionEvent(tr, eventType, accountTimeZone);
+                    final SubscriptionEvent event = toSubscriptionEvent(tr, eventType, accountTimeZone, internalTenantContext);
                     insertSubscriptionEvent(event, result);
                 }
             }
@@ -150,7 +151,7 @@ public class SubscriptionEventOrdering extends EntitlementOrderingBase {
         result.add(index, event);
     }
 
-    private SubscriptionEvent toSubscriptionEvent(final SubscriptionBaseTransition in, final SubscriptionEventType eventType, final DateTimeZone accountTimeZone) {
+    private SubscriptionEvent toSubscriptionEvent(final SubscriptionBaseTransition in, final SubscriptionEventType eventType, final DateTimeZone accountTimeZone, final InternalTenantContext internalTenantContext) {
         return new DefaultSubscriptionEvent(in.getId(),
                                             in.getSubscriptionId(),
                                             in.getEffectiveTransitionTime(),
@@ -170,7 +171,8 @@ public class SubscriptionEventOrdering extends EntitlementOrderingBase {
                                             in.getNextPriceList(),
                                             (in.getNextPlan() != null ? in.getNextPlan().getRecurringBillingPeriod() : null),
                                             in.getCreatedDate(),
-                                            accountTimeZone);
+                                            accountTimeZone,
+                                            internalTenantContext);
     }
 
     //
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 7bda096..bcce602 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
@@ -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
@@ -124,7 +124,6 @@ public class DefaultEntitlementApiBase {
     }
 
     public AccountEntitlements getAllEntitlementsForAccountId(final UUID accountId, final InternalTenantContext tenantContext) throws EntitlementApiException {
-
         final AccountEventsStreams accountEventsStreams = eventsStreamBuilder.buildForAccount(tenantContext);
 
         final Map<UUID, Collection<Entitlement>> entitlementsPerBundle = new HashMap<UUID, Collection<Entitlement>>();
@@ -136,7 +135,7 @@ public class DefaultEntitlementApiBase {
             for (final EventsStream eventsStream : accountEventsStreams.getEventsStreams().get(bundleId)) {
                 final Entitlement entitlement = new DefaultEntitlement(eventsStream, eventsStreamBuilder, entitlementApi, pluginExecution,
                                                                        blockingStateDao, subscriptionInternalApi, checker, notificationQueueService,
-                                                                       entitlementUtils, dateHelper, clock, securityApi, internalCallContextFactory);
+                                                                       entitlementUtils, dateHelper, clock, securityApi, tenantContext, internalCallContextFactory);
                 entitlementsPerBundle.get(bundleId).add(entitlement);
             }
         }
@@ -148,7 +147,7 @@ public class DefaultEntitlementApiBase {
         final EventsStream eventsStream = eventsStreamBuilder.buildForEntitlement(entitlementId, tenantContext);
         return new DefaultEntitlement(eventsStream, eventsStreamBuilder, entitlementApi, pluginExecution,
                                       blockingStateDao, subscriptionInternalApi, checker, notificationQueueService,
-                                      entitlementUtils, dateHelper, clock, securityApi, internalCallContextFactory);
+                                      entitlementUtils, dateHelper, clock, securityApi, tenantContext, internalCallContextFactory);
     }
 
     public void pause(final UUID bundleId, final LocalDate localEffectiveDate, final Iterable<PluginProperty> properties, final InternalCallContext internalCallContext) throws EntitlementApiException {
@@ -173,7 +172,7 @@ public class DefaultEntitlementApiBase {
                     final SubscriptionBase baseSubscription = subscriptionInternalApi.getBaseSubscription(bundleId, internalCallContext);
                     final DateTime effectiveDate = dateHelper.fromLocalDateAndReferenceTime(updatedPluginContext.getEffectiveDate(), baseSubscription.getStartDate(), internalCallContext);
 
-                    if (!dateHelper.isBeforeOrEqualsToday(effectiveDate, account.getTimeZone())) {
+                    if (!dateHelper.isBeforeOrEqualsToday(effectiveDate, account.getTimeZone(), internalCallContext)) {
                         recordPauseResumeNotificationEntry(baseSubscription.getId(), bundleId, effectiveDate, true, internalCallContext);
                         return null;
                     }
@@ -225,7 +224,7 @@ public class DefaultEntitlementApiBase {
 
                     final DateTime effectiveDate = dateHelper.fromLocalDateAndReferenceTime(updatedPluginContext.getEffectiveDate(), baseSubscription.getStartDate(), internalCallContext);
 
-                    if (!dateHelper.isBeforeOrEqualsToday(effectiveDate, account.getTimeZone())) {
+                    if (!dateHelper.isBeforeOrEqualsToday(effectiveDate, account.getTimeZone(), internalCallContext)) {
                         recordPauseResumeNotificationEntry(baseSubscription.getId(), bundleId, effectiveDate, false, internalCallContext);
                         return null;
                     }
diff --git a/entitlement/src/main/java/org/killbill/billing/entitlement/api/svcs/DefaultEntitlementInternalApi.java b/entitlement/src/main/java/org/killbill/billing/entitlement/api/svcs/DefaultEntitlementInternalApi.java
index d77e4ab..fbb664c 100644
--- a/entitlement/src/main/java/org/killbill/billing/entitlement/api/svcs/DefaultEntitlementInternalApi.java
+++ b/entitlement/src/main/java/org/killbill/billing/entitlement/api/svcs/DefaultEntitlementInternalApi.java
@@ -216,8 +216,7 @@ public class DefaultEntitlementInternalApi extends DefaultEntitlementApiBase imp
 
         @Override
         public Entitlement doCall(final EntitlementApi entitlementApi, final EntitlementContext updatedPluginContext) throws EntitlementApiException {
-            final LocalDate effectiveLocalDate = new LocalDate(updatedPluginContext.getEffectiveDate(), entitlement.getAccountTimeZone());
-            DateTime effectiveDate = dateHelper.fromLocalDateAndReferenceTime(effectiveLocalDate, entitlement.getSubscriptionBase().getStartDate(), internalCallContext);
+            DateTime effectiveDate = dateHelper.fromLocalDateAndReferenceTime(updatedPluginContext.getEffectiveDate(), entitlement.getSubscriptionBase().getStartDate(), internalCallContext);
             // Avoid timing issues for IMM cancellations (we don't want an entitlement cancel date one second or so after the subscription cancel date or
             // add-ons cancellations computations won't work).
             if (effectiveDate.compareTo(entitlement.getSubscriptionBase().getEndDate()) > 0) {
diff --git a/entitlement/src/main/java/org/killbill/billing/entitlement/DefaultEntitlementService.java b/entitlement/src/main/java/org/killbill/billing/entitlement/DefaultEntitlementService.java
index 7e189c0..15612fc 100644
--- a/entitlement/src/main/java/org/killbill/billing/entitlement/DefaultEntitlementService.java
+++ b/entitlement/src/main/java/org/killbill/billing/entitlement/DefaultEntitlementService.java
@@ -24,6 +24,9 @@ import java.util.Collection;
 import java.util.UUID;
 
 import org.joda.time.DateTime;
+import org.killbill.billing.account.api.AccountApiException;
+import org.killbill.billing.account.api.AccountInternalApi;
+import org.killbill.billing.account.api.ImmutableAccountData;
 import org.killbill.billing.callcontext.InternalCallContext;
 import org.killbill.billing.entitlement.api.BlockingState;
 import org.killbill.billing.entitlement.api.DefaultBlockingTransitionInternalEvent;
@@ -65,6 +68,7 @@ public class DefaultEntitlementService implements EntitlementService {
     private static final Logger log = LoggerFactory.getLogger(DefaultEntitlementService.class);
 
     private final EntitlementInternalApi entitlementInternalApi;
+    private final AccountInternalApi accountInternalApi;
     private final BlockingStateDao blockingStateDao;
     private final PersistentBus eventBus;
     private final NotificationQueueService notificationQueueService;
@@ -75,12 +79,14 @@ public class DefaultEntitlementService implements EntitlementService {
 
     @Inject
     public DefaultEntitlementService(final EntitlementInternalApi entitlementInternalApi,
+                                     final AccountInternalApi accountInternalApi,
                                      final BlockingStateDao blockingStateDao,
                                      final PersistentBus eventBus,
                                      final NotificationQueueService notificationQueueService,
                                      final EntitlementUtils entitlementUtils,
                                      final InternalCallContextFactory internalCallContextFactory) {
         this.entitlementInternalApi = entitlementInternalApi;
+        this.accountInternalApi = accountInternalApi;
         this.blockingStateDao = blockingStateDao;
         this.eventBus = eventBus;
         this.notificationQueueService = notificationQueueService;
@@ -136,15 +142,23 @@ public class DefaultEntitlementService implements EntitlementService {
             return;
         }
 
+        final ImmutableAccountData immutableAccountData;
+        try {
+            immutableAccountData = accountInternalApi.getImmutableAccountDataById(entitlement.getAccountId(), internalCallContext);
+        } catch (final AccountApiException e) {
+            log.error("Error processing event for entitlement {}" + entitlement.getId(), e);
+            return;
+        }
+
         final EntitlementNotificationKeyAction entitlementNotificationKeyAction = key.getEntitlementNotificationKeyAction();
         try {
             if (EntitlementNotificationKeyAction.CHANGE.equals(entitlementNotificationKeyAction) ||
                 EntitlementNotificationKeyAction.CANCEL.equals(entitlementNotificationKeyAction)) {
                 blockAddOnsIfRequired(key, (DefaultEntitlement) entitlement, callContext, internalCallContext);
             } else if (EntitlementNotificationKeyAction.PAUSE.equals(entitlementNotificationKeyAction)) {
-                entitlementInternalApi.pause(key.getBundleId(), key.getEffectiveDate().toLocalDate(), ImmutableList.<PluginProperty>of(), internalCallContext);
+                entitlementInternalApi.pause(key.getBundleId(), internalCallContext.toLocalDate(key.getEffectiveDate(), immutableAccountData.getTimeZone()), ImmutableList.<PluginProperty>of(), internalCallContext);
             } else if (EntitlementNotificationKeyAction.RESUME.equals(entitlementNotificationKeyAction)) {
-                entitlementInternalApi.resume(key.getBundleId(), key.getEffectiveDate().toLocalDate(), ImmutableList.<PluginProperty>of(), internalCallContext);
+                entitlementInternalApi.resume(key.getBundleId(), internalCallContext.toLocalDate(key.getEffectiveDate(), immutableAccountData.getTimeZone()), ImmutableList.<PluginProperty>of(), internalCallContext);
             }
         } catch (final EntitlementApiException e) {
             log.error("Error processing event for entitlement {}" + entitlement.getId(), e);
diff --git a/entitlement/src/main/java/org/killbill/billing/entitlement/engine/core/DefaultEventsStream.java b/entitlement/src/main/java/org/killbill/billing/entitlement/engine/core/DefaultEventsStream.java
index e7058f7..d428bdf 100644
--- a/entitlement/src/main/java/org/killbill/billing/entitlement/engine/core/DefaultEventsStream.java
+++ b/entitlement/src/main/java/org/killbill/billing/entitlement/engine/core/DefaultEventsStream.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:
  *
@@ -399,12 +401,12 @@ public class DefaultEventsStream implements EventsStream {
                                                                           return DefaultEntitlementApi.ENT_STATE_CANCELLED.equals(input.getStateName());
                                                                       }
                                                                   }).orNull();
-        entitlementEffectiveEndDate = entitlementCancelEvent != null ?  new LocalDate(entitlementCancelEvent.getEffectiveDate(), account.getTimeZone()) : null;
+        entitlementEffectiveEndDate = entitlementCancelEvent != null ? internalTenantContext.toLocalDate(entitlementCancelEvent.getEffectiveDate(), account.getTimeZone()) : null;
     }
 
     private void computeStateForEntitlement() {
         // Current state for the ENTITLEMENT_SERVICE_NAME is set to cancelled
-        if (entitlementEffectiveEndDate != null && entitlementEffectiveEndDate.compareTo(new LocalDate(utcNow, account.getTimeZone())) <= 0) {
+        if (entitlementEffectiveEndDate != null && entitlementEffectiveEndDate.compareTo(internalTenantContext.toLocalDate(utcNow, account.getTimeZone())) <= 0) {
             entitlementState = EntitlementState.CANCELLED;
         } else {
             final LocalDate startDate = new LocalDate(getSubscriptionBase().getStartDate(), account.getTimeZone());
diff --git a/entitlement/src/test/java/org/killbill/billing/entitlement/api/TestDefaultSubscriptionBundleTimeline.java b/entitlement/src/test/java/org/killbill/billing/entitlement/api/TestDefaultSubscriptionBundleTimeline.java
index 7c67a0e..f38553c 100644
--- a/entitlement/src/test/java/org/killbill/billing/entitlement/api/TestDefaultSubscriptionBundleTimeline.java
+++ b/entitlement/src/test/java/org/killbill/billing/entitlement/api/TestDefaultSubscriptionBundleTimeline.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:
  *
@@ -61,7 +63,7 @@ public class TestDefaultSubscriptionBundleTimeline extends EntitlementTestSuiteN
     public class TestSubscriptionBundleTimeline extends DefaultSubscriptionBundleTimeline {
 
         public TestSubscriptionBundleTimeline(final DateTimeZone accountTimeZone, final UUID accountId, final UUID bundleId, final String externalKey, final Iterable<Entitlement> entitlements) {
-            super(accountTimeZone, accountId, bundleId, externalKey, entitlements);
+            super(accountTimeZone, accountId, bundleId, externalKey, entitlements, internalCallContext);
         }
 
         public SubscriptionEvent createEvent(final UUID subscriptionId, final SubscriptionEventType type, final DateTime effectiveDate) {
@@ -84,7 +86,8 @@ public class TestDefaultSubscriptionBundleTimeline extends EntitlementTestSuiteN
                                                 null,
                                                 null,
                                                 null,
-                                                null);
+                                                null,
+                                                internalCallContext);
 
         }
     }
@@ -293,7 +296,7 @@ public class TestDefaultSubscriptionBundleTimeline extends EntitlementTestSuiteN
         final Entitlement entitlement = createEntitlement(entitlementId, allTransitions);
         entitlements.add(entitlement);
 
-        final SubscriptionBundleTimeline timeline = new DefaultSubscriptionBundleTimeline(accountTimeZone, accountId, bundleId, externalKey, entitlements);
+        final SubscriptionBundleTimeline timeline = new DefaultSubscriptionBundleTimeline(accountTimeZone, accountId, bundleId, externalKey, entitlements, internalCallContext);
 
         assertEquals(timeline.getAccountId(), accountId);
         assertEquals(timeline.getBundleId(), bundleId);
@@ -364,7 +367,7 @@ public class TestDefaultSubscriptionBundleTimeline extends EntitlementTestSuiteN
         final Entitlement entitlement = createEntitlement(entitlementId, allTransitions, blockingStates);
         entitlements.add(entitlement);
 
-        final SubscriptionBundleTimeline timeline = new DefaultSubscriptionBundleTimeline(accountTimeZone, accountId, bundleId, externalKey, entitlements);
+        final SubscriptionBundleTimeline timeline = new DefaultSubscriptionBundleTimeline(accountTimeZone, accountId, bundleId, externalKey, entitlements, internalCallContext);
 
         assertEquals(timeline.getAccountId(), accountId);
         assertEquals(timeline.getBundleId(), bundleId);
@@ -459,7 +462,7 @@ public class TestDefaultSubscriptionBundleTimeline extends EntitlementTestSuiteN
         final Entitlement entitlement = createEntitlement(entitlementId, allTransitions, blockingStates);
         entitlements.add(entitlement);
 
-        final SubscriptionBundleTimeline timeline = new DefaultSubscriptionBundleTimeline(accountTimeZone, accountId, bundleId, externalKey, entitlements);
+        final SubscriptionBundleTimeline timeline = new DefaultSubscriptionBundleTimeline(accountTimeZone, accountId, bundleId, externalKey, entitlements, internalCallContext);
 
         assertEquals(timeline.getAccountId(), accountId);
         assertEquals(timeline.getBundleId(), bundleId);
@@ -598,7 +601,7 @@ public class TestDefaultSubscriptionBundleTimeline extends EntitlementTestSuiteN
         final Entitlement entitlement = createEntitlement(entitlementId, allTransitions, blockingStates);
         entitlements.add(entitlement);
 
-        final SubscriptionBundleTimeline timeline = new DefaultSubscriptionBundleTimeline(accountTimeZone, accountId, bundleId, externalKey, entitlements);
+        final SubscriptionBundleTimeline timeline = new DefaultSubscriptionBundleTimeline(accountTimeZone, accountId, bundleId, externalKey, entitlements, internalCallContext);
 
         assertEquals(timeline.getAccountId(), accountId);
         assertEquals(timeline.getBundleId(), bundleId);
@@ -722,7 +725,7 @@ public class TestDefaultSubscriptionBundleTimeline extends EntitlementTestSuiteN
         final Entitlement entitlement = createEntitlement(entitlementId, allTransitions, blockingStates);
         entitlements.add(entitlement);
 
-        final SubscriptionBundleTimeline timeline = new DefaultSubscriptionBundleTimeline(accountTimeZone, accountId, bundleId, externalKey, entitlements);
+        final SubscriptionBundleTimeline timeline = new DefaultSubscriptionBundleTimeline(accountTimeZone, accountId, bundleId, externalKey, entitlements, internalCallContext);
 
         assertEquals(timeline.getAccountId(), accountId);
         assertEquals(timeline.getBundleId(), bundleId);
@@ -806,7 +809,7 @@ public class TestDefaultSubscriptionBundleTimeline extends EntitlementTestSuiteN
         final Entitlement entitlement = createEntitlement(entitlementId, allTransitions, blockingStates);
         entitlements.add(entitlement);
 
-        final SubscriptionBundleTimeline timeline = new DefaultSubscriptionBundleTimeline(accountTimeZone, accountId, bundleId, externalKey, entitlements);
+        final SubscriptionBundleTimeline timeline = new DefaultSubscriptionBundleTimeline(accountTimeZone, accountId, bundleId, externalKey, entitlements, internalCallContext);
 
         final List<SubscriptionEvent> events = timeline.getSubscriptionEvents();
         assertEquals(events.size(), 6);
@@ -904,7 +907,7 @@ public class TestDefaultSubscriptionBundleTimeline extends EntitlementTestSuiteN
         final Entitlement entitlement2 = createEntitlement(entitlementId2, allTransitions2, blockingStates);
         entitlements.add(entitlement2);
 
-        final SubscriptionBundleTimeline timeline = new DefaultSubscriptionBundleTimeline(accountTimeZone, accountId, bundleId, externalKey, entitlements);
+        final SubscriptionBundleTimeline timeline = new DefaultSubscriptionBundleTimeline(accountTimeZone, accountId, bundleId, externalKey, entitlements, internalCallContext);
 
         final List<SubscriptionEvent> events = timeline.getSubscriptionEvents();
         assertEquals(events.size(), 9);
@@ -1006,7 +1009,7 @@ public class TestDefaultSubscriptionBundleTimeline extends EntitlementTestSuiteN
 
         // Verify the timeline without the blocking state events
         final ImmutableList<Entitlement> entitlementsWithoutBlockingStates = ImmutableList.<Entitlement>of(createEntitlement(entitlementId, allTransitions, blockingStates));
-        final List<SubscriptionEvent> eventsWithoutBlockingStates = new DefaultSubscriptionBundleTimeline(accountTimeZone, accountId, bundleId, externalKey, entitlementsWithoutBlockingStates).getSubscriptionEvents();
+        final List<SubscriptionEvent> eventsWithoutBlockingStates = new DefaultSubscriptionBundleTimeline(accountTimeZone, accountId, bundleId, externalKey, entitlementsWithoutBlockingStates, internalCallContext).getSubscriptionEvents();
         assertEquals(eventsWithoutBlockingStates.size(), 4);
         assertEquals(eventsWithoutBlockingStates.get(0).getSubscriptionEventType(), SubscriptionEventType.START_ENTITLEMENT);
         assertEquals(eventsWithoutBlockingStates.get(1).getSubscriptionEventType(), SubscriptionEventType.START_BILLING);
@@ -1022,7 +1025,7 @@ public class TestDefaultSubscriptionBundleTimeline extends EntitlementTestSuiteN
 
         // Verify the timeline with the overdue event blocking the entitlement
         final ImmutableList<Entitlement> entitlementsWithOverdueEvent = ImmutableList.<Entitlement>of(createEntitlement(entitlementId, allTransitions, blockingStates));
-        final List<SubscriptionEvent> eventsWithOverdueEvent = new DefaultSubscriptionBundleTimeline(accountTimeZone, accountId, bundleId, externalKey, entitlementsWithOverdueEvent).getSubscriptionEvents();
+        final List<SubscriptionEvent> eventsWithOverdueEvent = new DefaultSubscriptionBundleTimeline(accountTimeZone, accountId, bundleId, externalKey, entitlementsWithOverdueEvent, internalCallContext).getSubscriptionEvents();
         assertEquals(eventsWithOverdueEvent.size(), 5);
         assertEquals(eventsWithOverdueEvent.get(0).getSubscriptionEventType(), SubscriptionEventType.START_ENTITLEMENT);
         assertEquals(eventsWithOverdueEvent.get(1).getSubscriptionEventType(), SubscriptionEventType.START_BILLING);
@@ -1041,7 +1044,7 @@ public class TestDefaultSubscriptionBundleTimeline extends EntitlementTestSuiteN
         entitlements.add(entitlement);
 
         // Verify the timeline with both the overdue event and the entitlement cancel event
-        final SubscriptionBundleTimeline timeline = new DefaultSubscriptionBundleTimeline(accountTimeZone, accountId, bundleId, externalKey, entitlements);
+        final SubscriptionBundleTimeline timeline = new DefaultSubscriptionBundleTimeline(accountTimeZone, accountId, bundleId, externalKey, entitlements, internalCallContext);
 
         assertEquals(timeline.getAccountId(), accountId);
         assertEquals(timeline.getBundleId(), bundleId);
@@ -1141,7 +1144,7 @@ public class TestDefaultSubscriptionBundleTimeline extends EntitlementTestSuiteN
         final Entitlement entitlement = createEntitlement(entitlementId, allTransitions, blockingStates);
         entitlements.add(entitlement);
 
-        final SubscriptionBundleTimeline timeline = new DefaultSubscriptionBundleTimeline(accountTimeZone, accountId, bundleId, externalKey, entitlements);
+        final SubscriptionBundleTimeline timeline = new DefaultSubscriptionBundleTimeline(accountTimeZone, accountId, bundleId, externalKey, entitlements, internalCallContext);
 
         final List<SubscriptionEvent> events = timeline.getSubscriptionEvents();
         assertEquals(events.size(), 4);
@@ -1234,7 +1237,7 @@ public class TestDefaultSubscriptionBundleTimeline extends EntitlementTestSuiteN
         final Entitlement entitlement = createEntitlement(entitlementId, allTransitions, blockingStates);
         entitlements.add(entitlement);
 
-        final SubscriptionBundleTimeline timeline = new DefaultSubscriptionBundleTimeline(accountTimeZone, accountId, bundleId, externalKey, entitlements);
+        final SubscriptionBundleTimeline timeline = new DefaultSubscriptionBundleTimeline(accountTimeZone, accountId, bundleId, externalKey, entitlements, internalCallContext);
 
         final List<SubscriptionEvent> events = timeline.getSubscriptionEvents();
         assertEquals(events.size(), 11);
diff --git a/entitlement/src/test/java/org/killbill/billing/entitlement/api/TestEntitlementDateHelper.java b/entitlement/src/test/java/org/killbill/billing/entitlement/api/TestEntitlementDateHelper.java
index ae889c9..c1bd678 100644
--- a/entitlement/src/test/java/org/killbill/billing/entitlement/api/TestEntitlementDateHelper.java
+++ b/entitlement/src/test/java/org/killbill/billing/entitlement/api/TestEntitlementDateHelper.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:
  *
@@ -149,6 +151,6 @@ public class TestEntitlementDateHelper extends EntitlementTestSuiteNoDB {
         // Check that our input date is greater than now
         assertTrue(inputDateEquals.compareTo(clock.getUTCNow()) > 0);
         // And yet since the LocalDate match the function returns true
-        assertTrue(dateHelper.isBeforeOrEqualsToday(inputDateEquals, timeZoneUtcMinus8));
+        assertTrue(dateHelper.isBeforeOrEqualsToday(inputDateEquals, timeZoneUtcMinus8, internalCallContext));
     }
 }

invoice/pom.xml 2(+1 -1)

diff --git a/invoice/pom.xml b/invoice/pom.xml
index ddba6d5..4614397 100644
--- a/invoice/pom.xml
+++ b/invoice/pom.xml
@@ -19,7 +19,7 @@
     <parent>
         <artifactId>killbill</artifactId>
         <groupId>org.kill-bill.billing</groupId>
-        <version>0.16.1-SNAPSHOT</version>
+        <version>0.16.2-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>killbill-invoice</artifactId>
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/api/svcs/DefaultInvoiceInternalApi.java b/invoice/src/main/java/org/killbill/billing/invoice/api/svcs/DefaultInvoiceInternalApi.java
index 80472a9..56565d2 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/api/svcs/DefaultInvoiceInternalApi.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/api/svcs/DefaultInvoiceInternalApi.java
@@ -20,7 +20,6 @@ package org.killbill.billing.invoice.api.svcs;
 
 import java.math.BigDecimal;
 import java.util.Collection;
-import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.UUID;
@@ -111,11 +110,6 @@ public class DefaultInvoiceInternalApi implements InvoiceInternalApi {
     }
 
     @Override
-    public InvoicePayment getInvoicePaymentForRefund(final UUID paymentId, final InternalTenantContext context) throws InvoiceApiException {
-        return getInvoicePayment(paymentId, InvoicePaymentType.REFUND, context);
-    }
-
-    @Override
     public InvoicePayment getInvoicePaymentForChargeback(final UUID paymentId, final InternalTenantContext context) throws InvoiceApiException {
         return getInvoicePayment(paymentId, InvoicePaymentType.CHARGED_BACK, context);
     }
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/dao/DefaultInvoiceDao.java b/invoice/src/main/java/org/killbill/billing/invoice/dao/DefaultInvoiceDao.java
index f22f497..5166e98 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/dao/DefaultInvoiceDao.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/dao/DefaultInvoiceDao.java
@@ -542,7 +542,7 @@ public class DefaultInvoiceDao extends EntityDaoBase<InvoiceModelDao, Invoice, I
                 }
                 // We expect the code to correctly pass the account currency -- the payment code, more generic accept chargeBack in different currencies,
                 // but this is only for direct payment (no invoice)
-                Preconditions.checkArgument(invoicePayment.getCurrency() == currency);
+                Preconditions.checkArgument(invoicePayment.getCurrency() == currency, String.format("Invoice payment currency %s doesn't match chargeback currency %s", invoicePayment.getCurrency(), currency));
 
                 final UUID invoicePaymentId = invoicePayment.getId();
                 final BigDecimal maxChargedBackAmount = invoiceDaoHelper.getRemainingAmountPaidFromTransaction(invoicePaymentId, entitySqlDaoWrapperFactory, context);
diff --git a/invoice/src/main/resources/org/killbill/billing/invoice/ddl.sql b/invoice/src/main/resources/org/killbill/billing/invoice/ddl.sql
index 1a3907c..f7cda2b 100644
--- a/invoice/src/main/resources/org/killbill/billing/invoice/ddl.sql
+++ b/invoice/src/main/resources/org/killbill/billing/invoice/ddl.sql
@@ -72,7 +72,6 @@ CREATE TABLE invoice_payments (
     PRIMARY KEY(record_id)
 ) /*! CHARACTER SET utf8 COLLATE utf8_bin */;
 CREATE UNIQUE INDEX invoice_payments_id ON invoice_payments(id);
-CREATE UNIQUE INDEX idx_invoice_payments ON invoice_payments(payment_id, type);
 CREATE INDEX invoice_payments_invoice_id ON invoice_payments(invoice_id);
 CREATE INDEX invoice_payments_reversals ON invoice_payments(linked_invoice_payment_id);
 CREATE INDEX invoice_payments_tenant_account_record_id ON invoice_payments(tenant_record_id, account_record_id);

jaxrs/pom.xml 2(+1 -1)

diff --git a/jaxrs/pom.xml b/jaxrs/pom.xml
index 0921bda..d0f56d5 100644
--- a/jaxrs/pom.xml
+++ b/jaxrs/pom.xml
@@ -21,7 +21,7 @@
     <parent>
         <artifactId>killbill</artifactId>
         <groupId>org.kill-bill.billing</groupId>
-        <version>0.16.1-SNAPSHOT</version>
+        <version>0.16.2-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>killbill-jaxrs</artifactId>

junction/pom.xml 2(+1 -1)

diff --git a/junction/pom.xml b/junction/pom.xml
index 8d3bcb6..422ded4 100644
--- a/junction/pom.xml
+++ b/junction/pom.xml
@@ -19,7 +19,7 @@
     <parent>
         <artifactId>killbill</artifactId>
         <groupId>org.kill-bill.billing</groupId>
-        <version>0.16.1-SNAPSHOT</version>
+        <version>0.16.2-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>killbill-junction</artifactId>
diff --git a/junction/src/main/java/org/killbill/billing/junction/plumbing/billing/BillCycleDayCalculator.java b/junction/src/main/java/org/killbill/billing/junction/plumbing/billing/BillCycleDayCalculator.java
index df27149..8a45819 100644
--- a/junction/src/main/java/org/killbill/billing/junction/plumbing/billing/BillCycleDayCalculator.java
+++ b/junction/src/main/java/org/killbill/billing/junction/plumbing/billing/BillCycleDayCalculator.java
@@ -141,8 +141,8 @@ public class BillCycleDayCalculator {
         }
 
         final DateTime date = plan.dateOfFirstRecurringNonZeroCharge(subscription.getStartDate(), initialPhaseType);
-        final int bcdUTC = date.toDateTime(DateTimeZone.UTC).getDayOfMonth();
-        final int bcdLocal = date.toDateTime(account.getTimeZone()).getDayOfMonth();
+        final int bcdUTC = context.toUTCDateTime(date).getDayOfMonth();
+        final int bcdLocal = context.toDateTime(date, account.getTimeZone()).getDayOfMonth();
         log.info("Calculated BCD: subscription id {}, subscription start {}, timezone {}, bcd UTC {}, bcd local {}",
                  subscription.getId(), date.toDateTimeISO(), account.getTimeZone(), bcdUTC, bcdLocal);
 
diff --git a/junction/src/main/java/org/killbill/billing/junction/plumbing/billing/BlockingCalculator.java b/junction/src/main/java/org/killbill/billing/junction/plumbing/billing/BlockingCalculator.java
index 3e36378..b43bd54 100644
--- a/junction/src/main/java/org/killbill/billing/junction/plumbing/billing/BlockingCalculator.java
+++ b/junction/src/main/java/org/killbill/billing/junction/plumbing/billing/BlockingCalculator.java
@@ -1,7 +1,8 @@
 /*
- * 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:
  *
@@ -33,8 +34,7 @@ import javax.annotation.Nullable;
 
 import org.joda.time.DateTime;
 import org.joda.time.DateTimeZone;
-
-import org.killbill.billing.account.api.Account;
+import org.joda.time.Days;
 import org.killbill.billing.callcontext.InternalTenantContext;
 import org.killbill.billing.catalog.api.BillingPeriod;
 import org.killbill.billing.catalog.api.Currency;
@@ -103,7 +103,6 @@ public class BlockingCalculator {
         final SortedSet<BillingEvent> billingEventsToAdd = new TreeSet<BillingEvent>();
         final SortedSet<BillingEvent> billingEventsToRemove = new TreeSet<BillingEvent>();
 
-
         final List<BlockingState> blockingEvents = blockingApi.getBlockingAllForAccount(context);
 
         final Iterable<BlockingState> accountBlockingEvents = Iterables.filter(blockingEvents, new Predicate<BlockingState>() {
@@ -118,7 +117,7 @@ public class BlockingCalculator {
 
         for (final UUID bundleId : bundleMap.keySet()) {
 
-            final List<BlockingState> bundleBlockingEvents = perBundleBlockingEvents.get(bundleId)  != null ? perBundleBlockingEvents.get(bundleId) : ImmutableList.<BlockingState>of();
+            final List<BlockingState> bundleBlockingEvents = perBundleBlockingEvents.get(bundleId) != null ? perBundleBlockingEvents.get(bundleId) : ImmutableList.<BlockingState>of();
 
             for (final SubscriptionBase subscription : bundleMap.get(bundleId)) {
                 // Avoid inserting additional events for subscriptions that don't even have a START event
@@ -144,10 +143,9 @@ public class BlockingCalculator {
         }
     }
 
-
     final List<BlockingState> getAggregateBlockingEventsPerSubscription(final Iterable<BlockingState> subscriptionBlockingEvents, final Iterable<BlockingState> bundleBlockingEvents, final Iterable<BlockingState> accountBlockingEvents) {
         final Iterable<BlockingState> tmp = Iterables.concat(subscriptionBlockingEvents, bundleBlockingEvents, accountBlockingEvents);
-        final List<BlockingState> result  = Lists.newArrayList(tmp);
+        final List<BlockingState> result = Lists.newArrayList(tmp);
         Collections.sort(result);
         return result;
     }
@@ -161,7 +159,7 @@ public class BlockingCalculator {
         });
 
         final Map<UUID, List<BlockingState>> perTypeBlockingEvents = new HashMap<UUID, List<BlockingState>>();
-        for  (final BlockingState cur : bundleBlockingEvents) {
+        for (final BlockingState cur : bundleBlockingEvents) {
             if (!perTypeBlockingEvents.containsKey(cur.getBlockedId())) {
                 perTypeBlockingEvents.put(cur.getBlockedId(), new ArrayList<BlockingState>());
             }
@@ -352,7 +350,7 @@ public class BlockingCalculator {
         final DateTime endDate = firstNonBlocking == null ? null : firstNonBlocking.getEffectiveDate();
         if (lastOne != null && lastOne.getEnd().compareTo(startDate) == 0) {
             lastOne.setEnd(endDate);
-        } else if (endDate == null || startDate.toLocalDate().compareTo(endDate.toLocalDate()) != 0) {
+        } else if (endDate == null || Days.daysBetween(startDate, endDate).getDays() >= 1) {
             // Don't disable for periods less than a day (see https://github.com/killbill/killbill/issues/267)
             result.add(new DisabledDuration(startDate, endDate));
         }

NEWS 3(+3 -0)

diff --git a/NEWS b/NEWS
index 2bb5bfe..8001aa9 100644
--- a/NEWS
+++ b/NEWS
@@ -1,3 +1,6 @@
+0.16.1
+    See https://github.com/killbill/killbill/releases/tag/killbill-0.16.1
+
 0.16.0
     TBD (point to 0.16 release page)
 

overdue/pom.xml 2(+1 -1)

diff --git a/overdue/pom.xml b/overdue/pom.xml
index c04472b..e7f373d 100644
--- a/overdue/pom.xml
+++ b/overdue/pom.xml
@@ -19,7 +19,7 @@
     <parent>
         <artifactId>killbill</artifactId>
         <groupId>org.kill-bill.billing</groupId>
-        <version>0.16.1-SNAPSHOT</version>
+        <version>0.16.2-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>killbill-overdue</artifactId>
diff --git a/overdue/src/main/java/org/killbill/billing/overdue/applicator/OverdueStateApplicator.java b/overdue/src/main/java/org/killbill/billing/overdue/applicator/OverdueStateApplicator.java
index 2c942f6..91a0e03 100644
--- a/overdue/src/main/java/org/killbill/billing/overdue/applicator/OverdueStateApplicator.java
+++ b/overdue/src/main/java/org/killbill/billing/overdue/applicator/OverdueStateApplicator.java
@@ -318,7 +318,7 @@ public class OverdueStateApplicator {
             computeEntitlementsToCancel(account, toBeCancelled, callContext);
 
             try {
-                entitlementInternalApi.cancel(toBeCancelled, new LocalDate(clock.getUTCNow(), account.getTimeZone()), actionPolicy, ImmutableList.<PluginProperty>of(), context);
+                entitlementInternalApi.cancel(toBeCancelled, clock.getToday(account.getTimeZone()), actionPolicy, ImmutableList.<PluginProperty>of(), context);
             } catch (final EntitlementApiException e) {
                 throw new OverdueException(e);
             }

payment/pom.xml 2(+1 -1)

diff --git a/payment/pom.xml b/payment/pom.xml
index 97119a8..de0ab68 100644
--- a/payment/pom.xml
+++ b/payment/pom.xml
@@ -19,7 +19,7 @@
     <parent>
         <artifactId>killbill</artifactId>
         <groupId>org.kill-bill.billing</groupId>
-        <version>0.16.1-SNAPSHOT</version>
+        <version>0.16.2-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>killbill-payment</artifactId>
diff --git a/payment/src/main/java/org/killbill/billing/payment/api/DefaultPayment.java b/payment/src/main/java/org/killbill/billing/payment/api/DefaultPayment.java
index f6c86e6..14e7ab0 100644
--- a/payment/src/main/java/org/killbill/billing/payment/api/DefaultPayment.java
+++ b/payment/src/main/java/org/killbill/billing/payment/api/DefaultPayment.java
@@ -72,21 +72,26 @@ public class DefaultPayment extends EntityBase implements Payment {
 
     private static BigDecimal getAmountForType(final Iterable<PaymentTransaction> transactions, final TransactionType transactiontype) {
         BigDecimal result = BigDecimal.ZERO;
-        final Iterable<PaymentTransaction> filtered = Iterables.filter(transactions, new Predicate<PaymentTransaction>() {
-            @Override
-            public boolean apply(final PaymentTransaction input) {
-                return input.getTransactionType() == transactiontype && TransactionStatus.SUCCESS.equals(input.getTransactionStatus());
+        BigDecimal processedResult = BigDecimal.ZERO;
+        boolean shouldUseProcessedAmount = true;
+
+        for (final PaymentTransaction transaction : transactions) {
+            if (transaction.getTransactionType() != transactiontype || !TransactionStatus.SUCCESS.equals(transaction.getTransactionStatus())) {
+                continue;
             }
-        });
-        if (TransactionType.AUTHORIZE.equals(transactiontype) && filtered.iterator().hasNext()) {
-            // HACK - For multi-step AUTH, don't sum the individual transactions
-            result = filtered.iterator().next().getAmount();
-        } else {
-            for (final PaymentTransaction dpt : filtered) {
-                result = result.add(dpt.getAmount());
+
+            result = result.add(transaction.getAmount());
+
+            shouldUseProcessedAmount = shouldUseProcessedAmount && transaction.getCurrency().equals(transaction.getProcessedCurrency()) && transaction.getProcessedAmount() != null;
+            processedResult = shouldUseProcessedAmount ? processedResult.add(transaction.getProcessedAmount()) : BigDecimal.ZERO;
+
+            // For multi-step AUTH, don't sum the individual transactions
+            if (TransactionType.AUTHORIZE.equals(transactiontype)) {
+                break;
             }
         }
-        return result;
+
+        return shouldUseProcessedAmount ? processedResult : result;
     }
 
     @Override
diff --git a/payment/src/main/java/org/killbill/billing/payment/core/janitor/IncompletePaymentTransactionTask.java b/payment/src/main/java/org/killbill/billing/payment/core/janitor/IncompletePaymentTransactionTask.java
index 15dc191..c71407d 100644
--- a/payment/src/main/java/org/killbill/billing/payment/core/janitor/IncompletePaymentTransactionTask.java
+++ b/payment/src/main/java/org/killbill/billing/payment/core/janitor/IncompletePaymentTransactionTask.java
@@ -211,11 +211,23 @@ public class IncompletePaymentTransactionTask extends CompletionTaskBase<Payment
         // Recompute new lastSuccessPaymentState. This is important to be able to allow new operations on the state machine (for e.g an AUTH_SUCCESS would now allow a CAPTURE operation)
         final String lastSuccessPaymentState = paymentStateMachineHelper.isSuccessState(newPaymentState) ? newPaymentState : null;
 
-        // Update the processedAmount, processedCurrency if we got a paymentTransactionInfoPlugin from the plugin and if this is a non error state
-        final BigDecimal processedAmount = (paymentTransactionInfoPlugin != null && isPendingOrFinalTransactionStatus(transactionStatus)) ?
-                                           paymentTransactionInfoPlugin.getAmount() : paymentTransaction.getProcessedAmount();
-        final Currency processedCurrency = (paymentTransactionInfoPlugin != null && isPendingOrFinalTransactionStatus(transactionStatus)) ?
-                                           paymentTransactionInfoPlugin.getCurrency() : paymentTransaction.getProcessedCurrency();
+        // Update processedAmount and processedCurrency
+        final BigDecimal processedAmount;
+        if (TransactionStatus.SUCCESS.equals(transactionStatus) || TransactionStatus.PENDING.equals(transactionStatus)) {
+            if (paymentTransactionInfoPlugin == null || paymentTransactionInfoPlugin.getAmount() == null) {
+                processedAmount = paymentTransaction.getProcessedAmount();
+            } else {
+                processedAmount = paymentTransactionInfoPlugin.getAmount();
+            }
+        } else {
+            processedAmount = BigDecimal.ZERO;
+        }
+        final Currency processedCurrency;
+        if (paymentTransactionInfoPlugin == null || paymentTransactionInfoPlugin.getCurrency() == null) {
+            processedCurrency = paymentTransaction.getProcessedCurrency();
+        } else {
+            processedCurrency = paymentTransactionInfoPlugin.getCurrency();
+        }
 
         // Update the gatewayErrorCode, gatewayError if we got a paymentTransactionInfoPlugin
         final String gatewayErrorCode = paymentTransactionInfoPlugin != null ? paymentTransactionInfoPlugin.getGatewayErrorCode() : paymentTransaction.getGatewayErrorCode();
@@ -239,12 +251,6 @@ public class IncompletePaymentTransactionTask extends CompletionTaskBase<Payment
         return (newTransactionStatus != TransactionStatus.UNKNOWN) ? newTransactionStatus : currentTransactionStatus;
     }
 
-    private boolean isPendingOrFinalTransactionStatus(final TransactionStatus transactionStatus) {
-        return (transactionStatus == TransactionStatus.PENDING ||
-                transactionStatus == TransactionStatus.SUCCESS ||
-                transactionStatus == TransactionStatus.PAYMENT_FAILURE);
-    }
-
     private PaymentPluginApi getPaymentPluginApi(final PaymentModelDao item, final String pluginName) {
         final PaymentPluginApi pluginApi = pluginRegistry.getServiceForName(pluginName);
         Preconditions.checkState(pluginApi != null, "Janitor IncompletePaymentTransactionTask cannot retrieve PaymentPluginApi for plugin %s (payment id %s), skipping", pluginName, item.getId());
diff --git a/payment/src/main/java/org/killbill/billing/payment/core/sm/PaymentAutomatonDAOHelper.java b/payment/src/main/java/org/killbill/billing/payment/core/sm/PaymentAutomatonDAOHelper.java
index 2b3a215..c6545dc 100644
--- a/payment/src/main/java/org/killbill/billing/payment/core/sm/PaymentAutomatonDAOHelper.java
+++ b/payment/src/main/java/org/killbill/billing/payment/core/sm/PaymentAutomatonDAOHelper.java
@@ -30,6 +30,7 @@ import org.killbill.billing.catalog.api.Currency;
 import org.killbill.billing.osgi.api.OSGIServiceRegistration;
 import org.killbill.billing.payment.api.PaymentApiException;
 import org.killbill.billing.payment.api.TransactionStatus;
+import org.killbill.billing.payment.api.TransactionType;
 import org.killbill.billing.payment.dao.PaymentDao;
 import org.killbill.billing.payment.dao.PaymentMethodModelDao;
 import org.killbill.billing.payment.dao.PaymentModelDao;
@@ -89,7 +90,10 @@ public class PaymentAutomatonDAOHelper {
             if (existingTransactions.isEmpty()) {
                 throw new PaymentApiException(ErrorCode.PAYMENT_NO_SUCH_SUCCESS_PAYMENT, paymentStateContext.getPaymentId());
             }
-            if (paymentStateContext.getCurrency() != null && existingTransactions.get(0).getCurrency() != paymentStateContext.getCurrency()) {
+            if (paymentStateContext.getCurrency() != null &&
+                existingTransactions.get(0).getCurrency() != paymentStateContext.getCurrency() &&
+                !TransactionType.CHARGEBACK.equals(paymentStateContext.getTransactionType())) {
+                // Note that we allow chargebacks in a different currency
                 throw new PaymentApiException(ErrorCode.PAYMENT_INVALID_PARAMETER, "currency", " should be " + existingTransactions.get(0).getCurrency() + " to match other existing transactions");
             }
 
@@ -103,8 +107,22 @@ public class PaymentAutomatonDAOHelper {
 
     public void processPaymentInfoPlugin(final TransactionStatus transactionStatus, @Nullable final PaymentTransactionInfoPlugin paymentInfoPlugin,
                                          final String currentPaymentStateName) {
-        final BigDecimal processedAmount = paymentInfoPlugin == null ? null : paymentInfoPlugin.getAmount();
-        final Currency processedCurrency = paymentInfoPlugin == null ? null : paymentInfoPlugin.getCurrency();
+        final BigDecimal processedAmount;
+        if (TransactionStatus.SUCCESS.equals(transactionStatus) || TransactionStatus.PENDING.equals(transactionStatus)) {
+            if (paymentInfoPlugin == null || paymentInfoPlugin.getAmount() == null) {
+                processedAmount = paymentStateContext.getAmount();
+            } else {
+                processedAmount = paymentInfoPlugin.getAmount();
+            }
+        } else {
+            processedAmount = BigDecimal.ZERO;
+        }
+        final Currency processedCurrency;
+        if (paymentInfoPlugin == null || paymentInfoPlugin.getCurrency() == null) {
+            processedCurrency = paymentStateContext.getCurrency();
+        } else {
+            processedCurrency = paymentInfoPlugin.getCurrency();
+        }
         final String gatewayErrorCode = paymentInfoPlugin == null ? null : paymentInfoPlugin.getGatewayErrorCode();
         final String gatewayErrorMsg = paymentInfoPlugin == null ? null : paymentInfoPlugin.getGatewayError();
 
diff --git a/payment/src/main/java/org/killbill/billing/payment/invoice/InvoicePaymentControlPluginApi.java b/payment/src/main/java/org/killbill/billing/payment/invoice/InvoicePaymentControlPluginApi.java
index 6a45550..7ccaba6 100644
--- a/payment/src/main/java/org/killbill/billing/payment/invoice/InvoicePaymentControlPluginApi.java
+++ b/payment/src/main/java/org/killbill/billing/payment/invoice/InvoicePaymentControlPluginApi.java
@@ -33,6 +33,7 @@ import org.killbill.billing.ErrorCode;
 import org.killbill.billing.ObjectType;
 import org.killbill.billing.callcontext.InternalCallContext;
 import org.killbill.billing.callcontext.InternalTenantContext;
+import org.killbill.billing.catalog.api.Currency;
 import org.killbill.billing.control.plugin.api.OnFailurePaymentControlResult;
 import org.killbill.billing.control.plugin.api.OnSuccessPaymentControlResult;
 import org.killbill.billing.control.plugin.api.PaymentApiType;
@@ -152,11 +153,19 @@ public final class InvoicePaymentControlPluginApi implements PaymentControlPlugi
                     final UUID invoiceId = getInvoiceId(pluginProperties);
                     existingInvoicePayment = invoiceApi.getInvoicePaymentForAttempt(paymentControlContext.getPaymentId(), internalContext);
                     if (existingInvoicePayment != null && existingInvoicePayment.isSuccess()) {
+                        // Only one successful purchase per payment (the invoice could be linked to multiple successful payments though)
                         log.info("onSuccessCall was already completed for payment purchase: " + paymentControlContext.getPaymentId());
                     } else {
-                        log.debug("Notifying invoice of successful payment: id={}, amount={}, currency={}, invoiceId={}", paymentControlContext.getPaymentId(), paymentControlContext.getProcessedAmount(), paymentControlContext.getCurrency(), invoiceId);
+                        final BigDecimal invoicePaymentAmount;
+                        if (paymentControlContext.getCurrency() == paymentControlContext.getProcessedCurrency()) {
+                            invoicePaymentAmount = paymentControlContext.getProcessedAmount();
+                        } else {
+                            log.warn("Currency {} of invoice payment {} doesn't match invoice currency {}, assuming it is a full payment" , paymentControlContext.getProcessedCurrency(), paymentControlContext.getPaymentId(), paymentControlContext.getCurrency());
+                            invoicePaymentAmount = paymentControlContext.getAmount();
+                        }
+                        log.debug("Notifying invoice of successful payment: id={}, amount={}, currency={}, invoiceId={}", paymentControlContext.getPaymentId(), invoicePaymentAmount, paymentControlContext.getCurrency(), invoiceId);
                         invoiceApi.notifyOfPayment(invoiceId,
-                                                   paymentControlContext.getProcessedAmount(),
+                                                   invoicePaymentAmount,
                                                    paymentControlContext.getCurrency(),
                                                    paymentControlContext.getProcessedCurrency(),
                                                    paymentControlContext.getPaymentId(),
@@ -167,23 +176,34 @@ public final class InvoicePaymentControlPluginApi implements PaymentControlPlugi
                     break;
 
                 case REFUND:
-                    existingInvoicePayment = invoiceApi.getInvoicePaymentForRefund(paymentControlContext.getPaymentId(), internalContext);
-                    if (existingInvoicePayment != null) {
-                        log.info("onSuccessCall was already completed for payment refund: " + paymentControlContext.getPaymentId());
-                    } else {
-                        final Map<UUID, BigDecimal> idWithAmount = extractIdsWithAmountFromProperties(pluginProperties);
-                        final PluginProperty prop = getPluginProperty(pluginProperties, PROP_IPCD_REFUND_WITH_ADJUSTMENTS);
-                        final boolean isAdjusted = prop != null ? Boolean.valueOf((String) prop.getValue()) : false;
-                        invoiceApi.createRefund(paymentControlContext.getPaymentId(), paymentControlContext.getAmount(), isAdjusted, idWithAmount, paymentControlContext.getTransactionExternalKey(), internalContext);
-                    }
+                    final Map<UUID, BigDecimal> idWithAmount = extractIdsWithAmountFromProperties(pluginProperties);
+                    final PluginProperty prop = getPluginProperty(pluginProperties, PROP_IPCD_REFUND_WITH_ADJUSTMENTS);
+                    final boolean isAdjusted = prop != null ? Boolean.valueOf((String) prop.getValue()) : false;
+                    invoiceApi.createRefund(paymentControlContext.getPaymentId(), paymentControlContext.getAmount(), isAdjusted, idWithAmount, paymentControlContext.getTransactionExternalKey(), internalContext);
                     break;
 
                 case CHARGEBACK:
                     existingInvoicePayment = invoiceApi.getInvoicePaymentForChargeback(paymentControlContext.getPaymentId(), internalContext);
                     if (existingInvoicePayment != null) {
+                        // We don't support partial chargebacks (yet?)
                         log.info("onSuccessCall was already completed for payment chargeback: " + paymentControlContext.getPaymentId());
                     } else {
-                        invoiceApi.createChargeback(paymentControlContext.getPaymentId(), paymentControlContext.getProcessedAmount(), paymentControlContext.getProcessedCurrency(), internalContext);
+                        final InvoicePayment linkedInvoicePayment = invoiceApi.getInvoicePaymentForAttempt(paymentControlContext.getPaymentId(), internalContext);
+
+                        final BigDecimal amount;
+                        final Currency currency;
+                        if (linkedInvoicePayment.getCurrency().equals(paymentControlContext.getProcessedCurrency()) && paymentControlContext.getProcessedAmount() != null) {
+                            amount = paymentControlContext.getProcessedAmount();
+                            currency = paymentControlContext.getProcessedCurrency();
+                        } else if (linkedInvoicePayment.getCurrency().equals(paymentControlContext.getCurrency()) && paymentControlContext.getAmount() != null) {
+                            amount = paymentControlContext.getAmount();
+                            currency = paymentControlContext.getCurrency();
+                        } else {
+                            amount = linkedInvoicePayment.getAmount();
+                            currency = linkedInvoicePayment.getCurrency();
+                        }
+
+                        invoiceApi.createChargeback(paymentControlContext.getPaymentId(), amount, currency, internalContext);
                     }
                     break;
 
@@ -209,7 +229,7 @@ public final class InvoicePaymentControlPluginApi implements PaymentControlPlugi
                 try {
                     log.debug("Notifying invoice of failed payment: id={}, amount={}, currency={}, invoiceId={}", paymentControlContext.getPaymentId(), paymentControlContext.getAmount(), paymentControlContext.getCurrency(), invoiceId);
                     invoiceApi.notifyOfPayment(invoiceId,
-                                               paymentControlContext.getAmount(),
+                                               BigDecimal.ZERO,
                                                paymentControlContext.getCurrency(),
                                                // processed currency may be null so we use currency; processed currency will be updated if/when payment succeeds
                                                paymentControlContext.getCurrency(),
diff --git a/payment/src/test/java/org/killbill/billing/payment/api/TestPaymentApi.java b/payment/src/test/java/org/killbill/billing/payment/api/TestPaymentApi.java
index d5c1aa5..d6e293e 100644
--- a/payment/src/test/java/org/killbill/billing/payment/api/TestPaymentApi.java
+++ b/payment/src/test/java/org/killbill/billing/payment/api/TestPaymentApi.java
@@ -182,7 +182,7 @@ public class TestPaymentApi extends PaymentTestSuiteWithEmbeddedDB {
         assertEquals(payment.getTransactions().get(0).getPaymentId(), payment.getId());
         assertEquals(payment.getTransactions().get(0).getAmount().compareTo(requestedAmount), 0);
         assertEquals(payment.getTransactions().get(0).getCurrency(), Currency.AED);
-        assertEquals(payment.getTransactions().get(0).getProcessedAmount().compareTo(requestedAmount), 0);
+        assertEquals(payment.getTransactions().get(0).getProcessedAmount().compareTo(BigDecimal.ZERO), 0);
         assertEquals(payment.getTransactions().get(0).getProcessedCurrency(), Currency.AED);
 
         assertEquals(payment.getTransactions().get(0).getTransactionStatus(), TransactionStatus.PAYMENT_FAILURE);
@@ -405,7 +405,7 @@ public class TestPaymentApi extends PaymentTestSuiteWithEmbeddedDB {
         assertEquals(payment.getTransactions().get(0).getPaymentId(), payment.getId());
         assertEquals(payment.getTransactions().get(0).getAmount().compareTo(requestedAmount), 0);
         assertEquals(payment.getTransactions().get(0).getCurrency(), Currency.USD);
-        assertEquals(payment.getTransactions().get(0).getProcessedAmount().compareTo(requestedAmount), 0); // This is weird...
+        assertEquals(payment.getTransactions().get(0).getProcessedAmount().compareTo(BigDecimal.ZERO), 0);
         assertEquals(payment.getTransactions().get(0).getProcessedCurrency(), Currency.USD);
 
         assertEquals(payment.getTransactions().get(0).getTransactionStatus(), TransactionStatus.PAYMENT_FAILURE);
diff --git a/payment/src/test/java/org/killbill/billing/payment/provider/MockPaymentProviderPlugin.java b/payment/src/test/java/org/killbill/billing/payment/provider/MockPaymentProviderPlugin.java
index 811a61e..cc8c6e1 100644
--- a/payment/src/test/java/org/killbill/billing/payment/provider/MockPaymentProviderPlugin.java
+++ b/payment/src/test/java/org/killbill/billing/payment/provider/MockPaymentProviderPlugin.java
@@ -29,6 +29,8 @@ import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.concurrent.atomic.AtomicInteger;
 import java.util.concurrent.atomic.AtomicReference;
 
+import javax.annotation.Nullable;
+
 import org.killbill.billing.catalog.api.Currency;
 import org.killbill.billing.payment.api.PaymentMethodPlugin;
 import org.killbill.billing.payment.api.PluginProperty;
@@ -71,6 +73,7 @@ public class MockPaymentProviderPlugin implements PaymentPluginApi {
     private final AtomicBoolean makeAllInvoicesFailWithError = new AtomicBoolean(false);
     private final AtomicInteger makePluginWaitSomeMilliseconds = new AtomicInteger(0);
     private final AtomicReference<BigDecimal> overrideNextProcessedAmount = new AtomicReference<BigDecimal>();
+    private final AtomicReference<Currency> overrideNextProcessedCurrency = new AtomicReference<Currency>();
 
     private final Map<String, InternalPaymentInfo> payments = new ConcurrentHashMap<String, InternalPaymentInfo>();
     private final Map<String, List<PaymentTransactionInfoPlugin>> paymentTransactions = new ConcurrentHashMap<String, List<PaymentTransactionInfoPlugin>>();
@@ -222,6 +225,10 @@ public class MockPaymentProviderPlugin implements PaymentPluginApi {
         overrideNextProcessedAmount.set(amount);
     }
 
+    public void overrideNextProcessedCurrency(final Currency currency) {
+        overrideNextProcessedCurrency.set(currency);
+    }
+
     public void updatePaymentTransactions(final UUID paymentId, final List<PaymentTransactionInfoPlugin> newTransactions) {
         if (paymentTransactions.containsKey(paymentId.toString())) {
             paymentTransactions.put (paymentId.toString(), newTransactions);
@@ -351,7 +358,7 @@ public class MockPaymentProviderPlugin implements PaymentPluginApi {
         return getPaymentTransactionInfoPluginResult(kbPaymentId, kbTransactionId, TransactionType.REFUND, refundAmount, currency, properties);
     }
 
-    private PaymentTransactionInfoPlugin getPaymentTransactionInfoPluginResult(final UUID kbPaymentId, final UUID kbTransactionId, final TransactionType type, final BigDecimal amount, final Currency currency, final Iterable<PluginProperty> pluginProperties) throws PaymentPluginApiException {
+    private PaymentTransactionInfoPlugin getPaymentTransactionInfoPluginResult(final UUID kbPaymentId, final UUID kbTransactionId, final TransactionType type, final BigDecimal amount, @Nullable final Currency currency, final Iterable<PluginProperty> pluginProperties) throws PaymentPluginApiException {
         if (makePluginWaitSomeMilliseconds.get() > 0) {
             try {
                 Thread.sleep(makePluginWaitSomeMilliseconds.get());
@@ -388,8 +395,12 @@ public class MockPaymentProviderPlugin implements PaymentPluginApi {
         }
 
         final BigDecimal processedAmount = MoreObjects.firstNonNull(overrideNextProcessedAmount.getAndSet(null), amount);
+        Currency processedCurrency = overrideNextProcessedCurrency.getAndSet(null);
+        if (processedCurrency == null) {
+            processedCurrency = currency;
+        }
 
-        final PaymentTransactionInfoPlugin result = new DefaultNoOpPaymentInfoPlugin(kbPaymentId, kbTransactionId, type, processedAmount, currency, clock.getUTCNow(), clock.getUTCNow(), status, errorCode, error);
+        final PaymentTransactionInfoPlugin result = new DefaultNoOpPaymentInfoPlugin(kbPaymentId, kbTransactionId, type, processedAmount, processedCurrency, clock.getUTCNow(), clock.getUTCNow(), status, errorCode, error);
         List<PaymentTransactionInfoPlugin> existingTransactions = paymentTransactions.get(kbPaymentId.toString());
         if (existingTransactions == null) {
             existingTransactions = new ArrayList<PaymentTransactionInfoPlugin>();

pom.xml 2(+1 -1)

diff --git a/pom.xml b/pom.xml
index 97f05eb..67ccff9 100644
--- a/pom.xml
+++ b/pom.xml
@@ -24,7 +24,7 @@
         <version>0.82-SNAPSHOT</version>
     </parent>
     <artifactId>killbill</artifactId>
-    <version>0.16.1-SNAPSHOT</version>
+    <version>0.16.2-SNAPSHOT</version>
     <packaging>pom</packaging>
     <name>killbill</name>
     <description>Library for managing recurring subscriptions and the associated billing</description>
diff --git a/profiles/killbill/pom.xml b/profiles/killbill/pom.xml
index 62c6d0c..81ad8e6 100644
--- a/profiles/killbill/pom.xml
+++ b/profiles/killbill/pom.xml
@@ -21,7 +21,7 @@
     <parent>
         <artifactId>killbill-profiles</artifactId>
         <groupId>org.kill-bill.billing</groupId>
-        <version>0.16.1-SNAPSHOT</version>
+        <version>0.16.2-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>killbill-profiles-killbill</artifactId>
diff --git a/profiles/killpay/pom.xml b/profiles/killpay/pom.xml
index 98b792d..6d43052 100644
--- a/profiles/killpay/pom.xml
+++ b/profiles/killpay/pom.xml
@@ -20,7 +20,7 @@
     <parent>
         <artifactId>killbill-profiles</artifactId>
         <groupId>org.kill-bill.billing</groupId>
-        <version>0.16.1-SNAPSHOT</version>
+        <version>0.16.2-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>killbill-profiles-killpay</artifactId>

profiles/pom.xml 2(+1 -1)

diff --git a/profiles/pom.xml b/profiles/pom.xml
index 5bbaef8..442ac18 100644
--- a/profiles/pom.xml
+++ b/profiles/pom.xml
@@ -19,7 +19,7 @@
     <parent>
         <artifactId>killbill</artifactId>
         <groupId>org.kill-bill.billing</groupId>
-        <version>0.16.1-SNAPSHOT</version>
+        <version>0.16.2-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>killbill-profiles</artifactId>
diff --git a/subscription/pom.xml b/subscription/pom.xml
index 0b9f959..825e97b 100644
--- a/subscription/pom.xml
+++ b/subscription/pom.xml
@@ -19,7 +19,7 @@
     <parent>
         <artifactId>killbill</artifactId>
         <groupId>org.kill-bill.billing</groupId>
-        <version>0.16.1-SNAPSHOT</version>
+        <version>0.16.2-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>killbill-subscription</artifactId>

tenant/pom.xml 2(+1 -1)

diff --git a/tenant/pom.xml b/tenant/pom.xml
index 793b2ce..a106e2b 100644
--- a/tenant/pom.xml
+++ b/tenant/pom.xml
@@ -19,7 +19,7 @@
     <parent>
         <artifactId>killbill</artifactId>
         <groupId>org.kill-bill.billing</groupId>
-        <version>0.16.1-SNAPSHOT</version>
+        <version>0.16.2-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>killbill-tenant</artifactId>

usage/pom.xml 2(+1 -1)

diff --git a/usage/pom.xml b/usage/pom.xml
index 5861678..b3dde9e 100644
--- a/usage/pom.xml
+++ b/usage/pom.xml
@@ -19,7 +19,7 @@
     <parent>
         <artifactId>killbill</artifactId>
         <groupId>org.kill-bill.billing</groupId>
-        <version>0.16.1-SNAPSHOT</version>
+        <version>0.16.2-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>killbill-usage</artifactId>

util/pom.xml 2(+1 -1)

diff --git a/util/pom.xml b/util/pom.xml
index 9a981a5..508a73f 100644
--- a/util/pom.xml
+++ b/util/pom.xml
@@ -21,7 +21,7 @@
     <parent>
         <artifactId>killbill</artifactId>
         <groupId>org.kill-bill.billing</groupId>
-        <version>0.16.1-SNAPSHOT</version>
+        <version>0.16.2-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>killbill-util</artifactId>
diff --git a/util/src/test/java/org/killbill/billing/api/TestApiListener.java b/util/src/test/java/org/killbill/billing/api/TestApiListener.java
index 690707a..d710a92 100644
--- a/util/src/test/java/org/killbill/billing/api/TestApiListener.java
+++ b/util/src/test/java/org/killbill/billing/api/TestApiListener.java
@@ -37,6 +37,7 @@ import org.killbill.billing.events.InvoiceCreationInternalEvent;
 import org.killbill.billing.events.InvoiceNotificationInternalEvent;
 import org.killbill.billing.events.InvoicePaymentErrorInternalEvent;
 import org.killbill.billing.events.InvoicePaymentInfoInternalEvent;
+import org.killbill.billing.events.NullInvoiceInternalEvent;
 import org.killbill.billing.events.PaymentErrorInternalEvent;
 import org.killbill.billing.events.PaymentInfoInternalEvent;
 import org.killbill.billing.events.PaymentPluginErrorInternalEvent;
@@ -113,6 +114,7 @@ public class TestApiListener {
         RESUME,
         PHASE,
         BLOCK,
+        NULL_INVOICE,
         INVOICE,
         INVOICE_NOTIFICATION,
         INVOICE_ADJUSTMENT,
@@ -239,6 +241,13 @@ public class TestApiListener {
     }
 
     @Subscribe
+    public void handleNullInvoiceEvents(final NullInvoiceInternalEvent event) {
+        log.info(String.format("Got Null Invoice event %s", event.toString()));
+        assertEqualsNicely(NextEvent.NULL_INVOICE);
+        notifyIfStackEmpty();
+    }
+
+    @Subscribe
     public void handleInvoiceEvents(final InvoiceCreationInternalEvent event) {
         log.info(String.format("Got Invoice event %s", event.toString()));
         assertEqualsNicely(NextEvent.INVOICE);