killbill-memoizeit

Changes

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

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

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

bin/start-server 2(+1 -1)

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

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

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

jaxrs/pom.xml 7(+6 -1)

jaxrs/src/main/java/org/killbill/billing/jaxrs/json/UsageJson.java 111(+0 -111)

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

NEWS 3(+3 -0)

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

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

payment/src/main/java/org/killbill/billing/payment/core/Janitor.java 281(+0 -281)

pom.xml 4(+2 -2)

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 2bc7a7b..1230ad0 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.11.10-SNAPSHOT</version>
+        <version>0.11.11-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>killbill-account</artifactId>

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

diff --git a/api/pom.xml b/api/pom.xml
index 9129b20..1032709 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.11.10-SNAPSHOT</version>
+        <version>0.11.11-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>killbill-internal-api</artifactId>

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

diff --git a/beatrix/pom.xml b/beatrix/pom.xml
index d6f1f37..6444b3a 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.11.10-SNAPSHOT</version>
+        <version>0.11.11-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>killbill-beatrix</artifactId>
diff --git a/beatrix/src/main/resources/org/killbill/billing/beatrix/ddl.sql b/beatrix/src/main/resources/org/killbill/billing/beatrix/ddl.sql
index 2783fc6..f7811c7 100644
--- a/beatrix/src/main/resources/org/killbill/billing/beatrix/ddl.sql
+++ b/beatrix/src/main/resources/org/killbill/billing/beatrix/ddl.sql
@@ -21,7 +21,7 @@ CREATE INDEX bus_ext_events_tenant_account_record_id ON bus_ext_events(search_ke
 
 DROP TABLE IF EXISTS bus_ext_events_history;
 CREATE TABLE bus_ext_events_history (
-    record_id int(11) unsigned NOT NULL AUTO_INCREMENT,
+    record_id int(11) unsigned NOT NULL,
     class_name varchar(128) NOT NULL,
     event_json varchar(2048) NOT NULL,
     user_token char(36),
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 84f8329..b5096a2 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
@@ -35,17 +35,16 @@ import org.killbill.billing.catalog.api.ProductCategory;
 import org.killbill.billing.entitlement.api.DefaultEntitlement;
 import org.killbill.billing.invoice.api.InvoiceItemType;
 import org.killbill.billing.usage.api.RolledUpUsage;
+import org.killbill.billing.usage.api.SubscriptionUsageRecord;
+import org.killbill.billing.usage.api.UnitUsageRecord;
+import org.killbill.billing.usage.api.UsageRecord;
 import org.killbill.billing.usage.api.UsageUserApi;
-import org.killbill.billing.usage.api.user.DefaultRolledUpUsage;
-import org.killbill.billing.usage.api.user.MockUsageUserApi;
 import org.killbill.billing.util.callcontext.CallContext;
 import org.killbill.billing.util.callcontext.TenantContext;
 import org.mockito.Mockito;
 import org.testng.annotations.BeforeMethod;
 import org.testng.annotations.Test;
 
-import com.google.common.collect.ImmutableList;
-
 public class TestConsumableInArrear extends TestIntegrationBase {
 
     @BeforeMethod(groups = "slow")
@@ -55,7 +54,7 @@ public class TestConsumableInArrear extends TestIntegrationBase {
 
     protected UsageUserApi createMockUsageUserApi(final List<RolledUpUsage> returnValue) {
         final UsageUserApi result = Mockito.mock(UsageUserApi.class);
-        Mockito.when(result.getAllUsageForSubscription(Mockito.<UUID>any(), Mockito.<Set<String>>any(), Mockito.<List<DateTime>>any(), Mockito.<TenantContext>any())).thenReturn(returnValue);
+        Mockito.when(result.getAllUsageForSubscription(Mockito.<UUID>any(), Mockito.<List<LocalDate>>any(), Mockito.<TenantContext>any())).thenReturn(returnValue);
         return result;
     }
 
@@ -85,8 +84,8 @@ public class TestConsumableInArrear extends TestIntegrationBase {
         //
         final DefaultEntitlement aoSubscription = addAOEntitlementAndCheckForCompletion(bpSubscription.getBundleId(), "Bullets", ProductCategory.ADD_ON, BillingPeriod.NO_BILLING_PERIOD, NextEvent.CREATE);
 
-        setUsage(aoSubscription.getId(), "bullets", new DateTime(2012, 4, 1, 1, 1, DateTimeZone.UTC), new DateTime(2012, 4, 15, 0, 0, DateTimeZone.UTC), new BigDecimal("99"), callContext);
-        setUsage(aoSubscription.getId(), "bullets", new DateTime(2012, 4, 15, 0, 0, DateTimeZone.UTC), new DateTime(2012, 4, 20, 1, 1, DateTimeZone.UTC), new BigDecimal("100"), callContext);
+        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);
         clock.setDay(new LocalDate(2012, 5, 1));
@@ -105,8 +104,8 @@ public class TestConsumableInArrear extends TestIntegrationBase {
                                     new ExpectedInvoiceItemCheck(new LocalDate(2012, 5, 1), new LocalDate(2012, 6, 1), InvoiceItemType.USAGE, BigDecimal.ZERO));
 
 
-        setUsage(aoSubscription.getId(), "bullets", new DateTime(2012, 6, 1, 1, 1, DateTimeZone.UTC), new DateTime(2012, 6, 15, 1, 1, DateTimeZone.UTC), new BigDecimal("50"), callContext);
-        setUsage(aoSubscription.getId(), "bullets", new DateTime(2012, 6, 16, 1, 1, DateTimeZone.UTC), new DateTime(2012, 6, 20, 1, 1, DateTimeZone.UTC), new BigDecimal("300"), callContext);
+        setUsage(aoSubscription.getId(), "bullets", new LocalDate(2012, 6, 1), 50L, callContext);
+        setUsage(aoSubscription.getId(), "bullets", new LocalDate(2012, 6, 16), 300L, callContext);
 
 
         busHandler.pushExpectedEvents(NextEvent.INVOICE, NextEvent.PAYMENT);
@@ -118,7 +117,12 @@ public class TestConsumableInArrear extends TestIntegrationBase {
 
     }
 
-    private void setUsage(final UUID subscriptionId, final String unitType, final DateTime startTime, final DateTime endTime, final BigDecimal amount, final CallContext context) {
-        usageUserApi.recordRolledUpUsage(subscriptionId, unitType, startTime, endTime, amount, context);
+    private void setUsage(final UUID subscriptionId, final String unitType, final LocalDate startDate, final Long amount, final CallContext context) {
+        final List<UsageRecord> usageRecords = new ArrayList<UsageRecord>();
+        usageRecords.add(new UsageRecord(startDate, amount));
+        final List<UnitUsageRecord> unitUsageRecords = new ArrayList<UnitUsageRecord>();
+        unitUsageRecords.add(new UnitUsageRecord(unitType, usageRecords));
+        final SubscriptionUsageRecord record = new SubscriptionUsageRecord(subscriptionId, unitUsageRecords);
+        usageUserApi.recordRolledUpUsage(record, context);
     }
 }

bin/start-server 2(+1 -1)

diff --git a/bin/start-server b/bin/start-server
index 755c267..407ef39 100755
--- a/bin/start-server
+++ b/bin/start-server
@@ -30,7 +30,7 @@ DEBUG_OPTS_ECLIPSE=" -Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=n,ad
 DEBUG_OPTS_ECLIPSE_WAIT=" -Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=12345 "
 
 # Default JVM settings if unset
-MAVEN_OPTS=${MAVEN_OPTS-"-Xms512m -Xmx1024m -XX:MaxPermSize=512m -XX:MaxDirectMemorySize=512m -XX:+UseConcMarkSweepGC"}
+MAVEN_OPTS=${MAVEN_OPTS-"-Dcom.sun.management.jmxremote.port=8989 -Dcom.sun.management.jmxremote=true -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false -Xms512m -Xmx1024m -XX:MaxPermSize=512m -XX:MaxDirectMemorySize=512m -XX:+UseConcMarkSweepGC"}
 
 LOG="$SERVER/src/main/resources/logback.xml"
 LOG_DIR="$SERVER/logs"

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

diff --git a/catalog/pom.xml b/catalog/pom.xml
index 129e5df..5d5f11d 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.11.10-SNAPSHOT</version>
+        <version>0.11.11-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>killbill-catalog</artifactId>

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

diff --git a/currency/pom.xml b/currency/pom.xml
index 1e5edc2..fd1692b 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.11.10-SNAPSHOT</version>
+        <version>0.11.11-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>killbill-currency</artifactId>
diff --git a/entitlement/pom.xml b/entitlement/pom.xml
index 30db7f4..5cf7590 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.11.10-SNAPSHOT</version>
+        <version>0.11.11-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>killbill-entitlement</artifactId>

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

diff --git a/invoice/pom.xml b/invoice/pom.xml
index d68dee7..5d3ddae 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.11.10-SNAPSHOT</version>
+        <version>0.11.11-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>killbill-invoice</artifactId>
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/generator/DefaultInvoiceGenerator.java b/invoice/src/main/java/org/killbill/billing/invoice/generator/DefaultInvoiceGenerator.java
index 89a9905..66eb264 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/generator/DefaultInvoiceGenerator.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/generator/DefaultInvoiceGenerator.java
@@ -135,7 +135,7 @@ public class DefaultInvoiceGenerator implements InvoiceGenerator {
 
                 final UUID subscriptionId = event.getSubscription().getId();
                 if (curSubscriptionId != null && !curSubscriptionId.equals(subscriptionId)) {
-                    final SubscriptionConsumableInArrear subscriptionConsumableInArrear = new SubscriptionConsumableInArrear(invoiceId, curEvents, usageApi, targetDate, context.toTenantContext(tenantId));
+                    final SubscriptionConsumableInArrear subscriptionConsumableInArrear = new SubscriptionConsumableInArrear(invoiceId, curEvents, usageApi, config.isInsertZeroUsageItems(), targetDate, context.toTenantContext(tenantId));
                     items.addAll(subscriptionConsumableInArrear.computeMissingUsageInvoiceItems(extractUsageItemsForSubscription(curSubscriptionId, existingInvoices)));
                     curEvents = Lists.newArrayList();
                 }
@@ -143,7 +143,7 @@ public class DefaultInvoiceGenerator implements InvoiceGenerator {
                 curEvents.add(event);
             }
             if (curSubscriptionId != null) {
-                final SubscriptionConsumableInArrear subscriptionConsumableInArrear = new SubscriptionConsumableInArrear(invoiceId, curEvents, usageApi, targetDate, context.toTenantContext(tenantId));
+                final SubscriptionConsumableInArrear subscriptionConsumableInArrear = new SubscriptionConsumableInArrear(invoiceId, curEvents, usageApi, config.isInsertZeroUsageItems(), targetDate, context.toTenantContext(tenantId));
                 items.addAll(subscriptionConsumableInArrear.computeMissingUsageInvoiceItems(extractUsageItemsForSubscription(curSubscriptionId, existingInvoices)));
             }
             return items;
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/usage/ContiguousIntervalConsumableInArrear.java b/invoice/src/main/java/org/killbill/billing/invoice/usage/ContiguousIntervalConsumableInArrear.java
index 69beb39..2b6349b 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/usage/ContiguousIntervalConsumableInArrear.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/usage/ContiguousIntervalConsumableInArrear.java
@@ -18,15 +18,12 @@ package org.killbill.billing.invoice.usage;
 
 import java.math.BigDecimal;
 import java.util.Collections;
-import java.util.HashMap;
-import java.util.LinkedList;
+import java.util.Comparator;
 import java.util.List;
-import java.util.Map;
 import java.util.Set;
 import java.util.UUID;
 import java.util.concurrent.atomic.AtomicBoolean;
 
-import org.joda.time.DateTime;
 import org.joda.time.DateTimeZone;
 import org.joda.time.LocalDate;
 import org.killbill.billing.catalog.api.CatalogApiException;
@@ -38,15 +35,16 @@ import org.killbill.billing.invoice.api.InvoiceItemType;
 import org.killbill.billing.invoice.generator.BillingIntervalDetail;
 import org.killbill.billing.invoice.model.UsageInvoiceItem;
 import org.killbill.billing.junction.BillingEvent;
+import org.killbill.billing.usage.api.RolledUpUnit;
 import org.killbill.billing.usage.api.RolledUpUsage;
 import org.killbill.billing.usage.api.UsageUserApi;
 import org.killbill.billing.util.callcontext.TenantContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 import com.google.common.annotations.VisibleForTesting;
-import com.google.common.base.Function;
 import com.google.common.base.Preconditions;
 import com.google.common.base.Predicate;
-import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Iterables;
 import com.google.common.collect.Lists;
 
@@ -59,6 +57,8 @@ import static org.killbill.billing.invoice.usage.UsageUtils.getConsumableInArrea
  */
 public class ContiguousIntervalConsumableInArrear {
 
+    private static final Logger log = LoggerFactory.getLogger(ContiguousIntervalConsumableInArrear.class);
+
     private final List<LocalDate> transitionTimes;
     private final List<BillingEvent> billingEvents;
 
@@ -69,12 +69,14 @@ public class ContiguousIntervalConsumableInArrear {
     private final UUID invoiceId;
     private final TenantContext context;
     private final AtomicBoolean isBuilt;
+    private final boolean insertZeroAmountItems;
 
-    public ContiguousIntervalConsumableInArrear(final Usage usage, final UUID invoiceId, final UsageUserApi usageApi, final LocalDate targetDate, final TenantContext context) {
+    public ContiguousIntervalConsumableInArrear(final Usage usage, final UUID invoiceId, final UsageUserApi usageApi, final boolean insertZeroAmountItems, final LocalDate targetDate, final TenantContext context) {
         this.usage = usage;
         this.invoiceId = invoiceId;
         this.unitTypes = getConsumableInArrearUnitTypes(usage);
         this.usageApi = usageApi;
+        this.insertZeroAmountItems = insertZeroAmountItems;
         this.targetDate = targetDate;
         this.context = context;
         this.billingEvents = Lists.newLinkedList();
@@ -134,35 +136,39 @@ public class ContiguousIntervalConsumableInArrear {
 
         final List<InvoiceItem> result = Lists.newLinkedList();
 
-        final RolledUpUsageForUnitTypesFactory factory = new RolledUpUsageForUnitTypesFactory(getRolledUpUsage(), unitTypes, getAccountTimeZone());
-
-        for (RolledUpUsageForUnitTypes ru : factory.getOrderedRolledUpUsageForUnitTypes()) {
+        final List<RolledUpUsage> allUsage = getRolledUpUsage();
+        for (final RolledUpUsage ru : allUsage) {
 
             // Compute total price amount that should be billed for that period of time (and usage section) across unitTypes.
             BigDecimal toBeBilledUsage = BigDecimal.ZERO;
-            for (final String unitType : unitTypes) {
-                final BigDecimal usageAmountForUnitType = ru.getUsageAmountForUnitType(unitType);
-                final BigDecimal toBeBilledForUnit = computeToBeBilledUsage(usageAmountForUnitType, unitType);
+            for (final RolledUpUnit cur : ru.getRolledUpUnit()) {
+                if (!unitTypes.contains(cur.getUnitType())) {
+                    log.warn("ContiguousIntervalConsumableInArrear is skipping unitType " + cur.getUnitType());
+                    continue;
+                }
+
+                final BigDecimal toBeBilledForUnit = computeToBeBilledUsage(cur.getAmount(), cur.getUnitType());
                 toBeBilledUsage = toBeBilledUsage.add(toBeBilledForUnit);
+
             }
             // Retrieves current price amount billed for that period of time (and usage section)
-            final Iterable<InvoiceItem> billedItems = getBilledItems(ru.getStartDate(), ru.getEndDate(), existingUsage);
+            final Iterable<InvoiceItem> billedItems = getBilledItems(ru.getStart(), ru.getEnd(), existingUsage);
             final BigDecimal billedUsage = computeBilledUsage(billedItems);
 
-            // Compare the two and add the missing piece if required. If there has never been any billed item for the period
-            // and if there is nothing to bill for we would also insert a $0 amount
+            // Compare the two and add the missing piece if required.
             if (!billedItems.iterator().hasNext() || billedUsage.compareTo(toBeBilledUsage) < 0) {
-                InvoiceItem item = new UsageInvoiceItem(invoiceId, getAccountId(), getBundleId(), getSubscriptionId(), getPlanName(),
-                                                        getPhaseName(), usage.getName(), ru.getStartDate(), ru.getEndDate(), toBeBilledUsage.subtract(billedUsage), getCurrency());
-                result.add(item);
+                final BigDecimal amountToBill = toBeBilledUsage.subtract(billedUsage);
+                if (amountToBill.compareTo(BigDecimal.ZERO) > 0 || insertZeroAmountItems) {
+                    InvoiceItem item = new UsageInvoiceItem(invoiceId, getAccountId(), getBundleId(), getSubscriptionId(), getPlanName(),
+                                                            getPhaseName(), usage.getName(), ru.getStart(), ru.getEnd(), amountToBill, getCurrency());
+                    result.add(item);
+                }
             }
-
         }
         return result;
     }
 
     /**
-     *
      * @return a list of {@code RolledUpUsage} for each period (between two transitions) * each unitType.
      */
     List<RolledUpUsage> getRolledUpUsage() {
@@ -170,25 +176,17 @@ public class ContiguousIntervalConsumableInArrear {
         if (transitionTimes.size() <= 1) {
             return Collections.emptyList();
         }
-
-        final Iterable<DateTime> transitions = Iterables.transform(transitionTimes, new Function<LocalDate, DateTime>() {
-            @Override
-            public DateTime apply(final LocalDate input) {
-                return localDateToEndOfDayInAccountTimezone(input, getAccountTimeZone());
-            }
-        });
-        return usageApi.getAllUsageForSubscription(getSubscriptionId(), unitTypes, ImmutableList.copyOf(transitions), context);
+        return usageApi.getAllUsageForSubscription(getSubscriptionId(), transitionTimes, context);
     }
 
     /**
-     *
      * @param nbUnits  the number of used units for a given period
      * @param unitType the type of unit
-     * @return  the price amount that should be billed for that period/unitType
+     * @return the price amount that should be billed for that period/unitType
      * @throws CatalogApiException
      */
     @VisibleForTesting
-    BigDecimal computeToBeBilledUsage(final BigDecimal nbUnits, final String unitType) throws CatalogApiException {
+    BigDecimal computeToBeBilledUsage(final Long nbUnits, final String unitType) throws CatalogApiException {
 
         Preconditions.checkState(isBuilt.get());
 
@@ -213,7 +211,6 @@ public class ContiguousIntervalConsumableInArrear {
     }
 
     /**
-     *
      * @param filteredUsageForInterval the list of invoiceItem to consider
      * @return the price amount that was already billed for that period and usage section (across unitTypes)
      */
@@ -238,7 +235,6 @@ public class ContiguousIntervalConsumableInArrear {
                 if (input.getInvoiceItemType() != InvoiceItemType.USAGE) {
                     return false;
                 }
-
                 // STEPH what happens if we discover usage period that overlap (one side or both side) the [startDate, endDate] interval
                 final UsageInvoiceItem usageInput = (UsageInvoiceItem) input;
                 return usageInput.getUsageName().equals(usage.getName()) &&
@@ -253,97 +249,6 @@ public class ContiguousIntervalConsumableInArrear {
         return transitionTimes;
     }
 
-    private static class RolledUpUsageForUnitTypesFactory {
-
-        private final Map<String, RolledUpUsageForUnitTypes> map;
-
-        public RolledUpUsageForUnitTypesFactory(final List<RolledUpUsage> rolledUpUsages, final Set<String> unitTypes, final DateTimeZone accountTimeZone) {
-            map = new HashMap<String, RolledUpUsageForUnitTypes>();
-            for (RolledUpUsage ru : rolledUpUsages) {
-
-                final LocalDate startRolledUpDate = new LocalDate(ru.getStartTime(), accountTimeZone);
-                final LocalDate endRolledUpDate = new LocalDate(ru.getEndTime(), accountTimeZone);
-                final String key = startRolledUpDate + "-" + endRolledUpDate;
-
-                RolledUpUsageForUnitTypes usageForUnitTypes = map.get(key);
-                if (usageForUnitTypes == null) {
-                    usageForUnitTypes = new RolledUpUsageForUnitTypes(startRolledUpDate, endRolledUpDate, unitTypes);
-                    map.put(key, usageForUnitTypes);
-                }
-                usageForUnitTypes.addUsageForUnit(ru.getUnitType(), ru.getAmount());
-            }
-        }
-
-        public List<RolledUpUsageForUnitTypes> getOrderedRolledUpUsageForUnitTypes() {
-            final LinkedList<RolledUpUsageForUnitTypes> result = new LinkedList<RolledUpUsageForUnitTypes>(map.values());
-            Collections.sort(result);
-            return result;
-        }
-    }
-
-    /**
-     * Internal classes to transform RolledUpUsage into a map of usage (types, amount) across each billable interval.
-     */
-    private static class RolledUpUsageForUnitTypes implements Comparable {
-
-        private final LocalDate startDate;
-        private final LocalDate endDate;
-        private final Map<String, BigDecimal> unitAmounts;
-
-        private RolledUpUsageForUnitTypes(final LocalDate startDate, final LocalDate endDate, final Set<String> unitTypes) {
-            this.endDate = endDate;
-            this.startDate = startDate;
-            this.unitAmounts = new HashMap<String, BigDecimal>();
-            for (final String type : unitTypes) {
-                unitAmounts.put(type, BigDecimal.ZERO);
-            }
-        }
-
-        public void addUsageForUnit(final String unitType, BigDecimal amount) {
-            final BigDecimal currentAmount = unitAmounts.get(unitType);
-            unitAmounts.put(unitType, currentAmount.add(amount));
-        }
-
-        public LocalDate getStartDate() {
-            return startDate;
-        }
-
-        public LocalDate getEndDate() {
-            return endDate;
-        }
-
-        public BigDecimal getUsageAmountForUnitType(final String unitType) {
-            return unitAmounts.get(unitType);
-        }
-
-        @Override
-        public int hashCode() {
-            int result = startDate != null ? startDate.hashCode() : 0;
-            result = 31 * result + (endDate != null ? endDate.hashCode() : 0);
-            result = 31 * result + (unitAmounts != null ? unitAmounts.hashCode() : 0);
-            return result;
-        }
-
-        @Override
-        public int compareTo(final Object o) {
-
-            Preconditions.checkArgument(o instanceof RolledUpUsageForUnitTypes);
-            final RolledUpUsageForUnitTypes other = (RolledUpUsageForUnitTypes) o;
-            // We will check later intervals don't overlap.
-            int i = getEndDate().compareTo(other.getStartDate());
-            if (i != 0) {
-                return i;
-            } else {
-                i = getEndDate().compareTo(other.getEndDate());
-                if (i != 0) {
-                    return i;
-                } else {
-                    return getStartDate().compareTo(other.getStartDate());
-                }
-            }
-        }
-    }
-
     public void addBillingEvent(final BillingEvent event) {
         Preconditions.checkState(!isBuilt.get());
         billingEvents.add(event);
@@ -386,9 +291,4 @@ public class ContiguousIntervalConsumableInArrear {
         return billingEvents.get(0).getTimeZone();
     }
 
-    static DateTime localDateToEndOfDayInAccountTimezone(final LocalDate input, final DateTimeZone accountTimeZone) {
-        final DateTime dateTimeInAccountTimeZone = new DateTime(input.getYear(), input.getMonthOfYear(), input.getDayOfMonth(), 0, 0, 0, accountTimeZone);
-        return new DateTime(dateTimeInAccountTimeZone, DateTimeZone.UTC);
-    }
-
 }
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/usage/SubscriptionConsumableInArrear.java b/invoice/src/main/java/org/killbill/billing/invoice/usage/SubscriptionConsumableInArrear.java
index 6707195..8da11ed 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/usage/SubscriptionConsumableInArrear.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/usage/SubscriptionConsumableInArrear.java
@@ -24,8 +24,6 @@ import java.util.Map;
 import java.util.Set;
 import java.util.UUID;
 
-import javax.annotation.Nullable;
-
 import org.joda.time.LocalDate;
 import org.killbill.billing.catalog.api.BillingMode;
 import org.killbill.billing.catalog.api.CatalogApiException;
@@ -39,7 +37,6 @@ import org.killbill.billing.util.callcontext.TenantContext;
 import com.google.common.annotations.VisibleForTesting;
 import com.google.common.base.Function;
 import com.google.common.collect.Collections2;
-import com.google.common.collect.Iterables;
 import com.google.common.collect.Lists;
 
 /**
@@ -52,11 +49,13 @@ public class SubscriptionConsumableInArrear {
     private final UsageUserApi usageApi;
     private final LocalDate targetDate;
     private final TenantContext context;
+    private final boolean insertZeroAmountItems;
 
-    public SubscriptionConsumableInArrear(final UUID invoiceId, final List<BillingEvent> subscriptionBillingEvents, final UsageUserApi usageApi, final LocalDate targetDate, final TenantContext context) {
+    public SubscriptionConsumableInArrear(final UUID invoiceId, final List<BillingEvent> subscriptionBillingEvents, final UsageUserApi usageApi, final boolean insertZeroAmountItems, LocalDate targetDate, final TenantContext context) {
         this.invoiceId = invoiceId;
         this.subscriptionBillingEvents = subscriptionBillingEvents;
         this.usageApi = usageApi;
+        this.insertZeroAmountItems = insertZeroAmountItems;
         this.targetDate = targetDate;
         this.context = context;
     }
@@ -107,7 +106,7 @@ public class SubscriptionConsumableInArrear {
                 // Add inflight usage interval if non existent
                 ContiguousIntervalConsumableInArrear existingInterval = inFlightInArrearUsageIntervals.get(usage.getName());
                 if (existingInterval == null) {
-                    existingInterval = new ContiguousIntervalConsumableInArrear(usage, invoiceId, usageApi, targetDate, context);
+                    existingInterval = new ContiguousIntervalConsumableInArrear(usage, invoiceId, usageApi, insertZeroAmountItems, targetDate, context);
                     inFlightInArrearUsageIntervals.put(usage.getName(), existingInterval);
                 }
                 // Add billing event for that usage interval
diff --git a/invoice/src/test/java/org/killbill/billing/invoice/generator/TestDefaultInvoiceGenerator.java b/invoice/src/test/java/org/killbill/billing/invoice/generator/TestDefaultInvoiceGenerator.java
index 52a1112..33a14d3 100644
--- a/invoice/src/test/java/org/killbill/billing/invoice/generator/TestDefaultInvoiceGenerator.java
+++ b/invoice/src/test/java/org/killbill/billing/invoice/generator/TestDefaultInvoiceGenerator.java
@@ -106,6 +106,11 @@ public class TestDefaultInvoiceGenerator extends InvoiceTestSuiteNoDB {
             public boolean isEmailNotificationsEnabled() {
                 return false;
             }
+
+            @Override
+            public boolean isInsertZeroUsageItems() {
+                return true;
+            }
         };
         this.generator = new DefaultInvoiceGenerator(clock, null, invoiceConfig, null, controllerDispatcher);
     }
diff --git a/invoice/src/test/java/org/killbill/billing/invoice/template/formatters/TestDefaultInvoiceItemFormatter.java b/invoice/src/test/java/org/killbill/billing/invoice/template/formatters/TestDefaultInvoiceItemFormatter.java
index eec566d..ca988e3 100644
--- a/invoice/src/test/java/org/killbill/billing/invoice/template/formatters/TestDefaultInvoiceItemFormatter.java
+++ b/invoice/src/test/java/org/killbill/billing/invoice/template/formatters/TestDefaultInvoiceItemFormatter.java
@@ -111,7 +111,7 @@ public class TestDefaultInvoiceItemFormatter extends InvoiceTestSuiteNoDB {
 
     private void checkOutput(final InvoiceItem invoiceItem, final String template, final String expected, final Locale locale) {
         final Map<String, Object> data = new HashMap<String, Object>();
-        data.put("invoiceItem", new DefaultInvoiceItemFormatter(config, invoiceItem, DateTimeFormat.mediumDate(), locale));
+        data.put("invoiceItem", new DefaultInvoiceItemFormatter(config, invoiceItem,  DateTimeFormat.mediumDate().withLocale(locale), locale));
 
         final String formattedText = templateEngine.executeTemplateText(template, data);
         Assert.assertEquals(formattedText, expected);
diff --git a/invoice/src/test/java/org/killbill/billing/invoice/usage/TestContiguousIntervalConsumableInArrear.java b/invoice/src/test/java/org/killbill/billing/invoice/usage/TestContiguousIntervalConsumableInArrear.java
index 5cb31a9..d73bcbe 100644
--- a/invoice/src/test/java/org/killbill/billing/invoice/usage/TestContiguousIntervalConsumableInArrear.java
+++ b/invoice/src/test/java/org/killbill/billing/invoice/usage/TestContiguousIntervalConsumableInArrear.java
@@ -35,6 +35,7 @@ import org.killbill.billing.invoice.api.InvoiceItem;
 import org.killbill.billing.invoice.model.FixedPriceInvoiceItem;
 import org.killbill.billing.invoice.model.UsageInvoiceItem;
 import org.killbill.billing.junction.BillingEvent;
+import org.killbill.billing.usage.api.RolledUpUnit;
 import org.killbill.billing.usage.api.RolledUpUsage;
 import org.killbill.billing.usage.api.user.DefaultRolledUpUsage;
 import org.testng.annotations.BeforeClass;
@@ -115,7 +116,7 @@ public class TestContiguousIntervalConsumableInArrear extends TestUsageInArrearB
                                                                                                                                                   Collections.<Usage>emptyList())
                                                                                                                           );
 
-        final BigDecimal result = intervalConsumableInArrear.computeToBeBilledUsage(new BigDecimal("5325"), "unit");
+        final BigDecimal result = intervalConsumableInArrear.computeToBeBilledUsage(5325L, "unit");
 
         // 5000 = 1000 (tier1) + 4325 (tier2) => 10 + 5 = 15
         assertEquals(result, new BigDecimal("15"));
@@ -129,13 +130,17 @@ public class TestContiguousIntervalConsumableInArrear extends TestUsageInArrearB
         final LocalDate endDate = new LocalDate(2014, 05, 15);
 
         // 2 items for startDate - firstBCDDate
-        final RolledUpUsage usage1 = new DefaultRolledUpUsage(subscriptionId, "unit", startDate.toDateTimeAtStartOfDay(DateTimeZone.UTC), firstBCDDate.toDateTimeAtStartOfDay(DateTimeZone.UTC), new BigDecimal("130"));
-        final RolledUpUsage usage2 = new DefaultRolledUpUsage(subscriptionId, "unit", startDate.toDateTimeAtStartOfDay(DateTimeZone.UTC), firstBCDDate.toDateTimeAtStartOfDay(DateTimeZone.UTC), new BigDecimal("271"));
+        final List<RolledUpUnit> units1 = new ArrayList<RolledUpUnit>();
+        units1.add(createRolledUpUnit("unit", 130L));
+        units1.add(createRolledUpUnit("unit", 271L));
+        final RolledUpUsage usage1 = new DefaultRolledUpUsage(subscriptionId, startDate, firstBCDDate, units1);
 
         // 1 items for firstBCDDate - endDate
-        final RolledUpUsage usage3 = new DefaultRolledUpUsage(subscriptionId, "unit", firstBCDDate.toDateTimeAtStartOfDay(DateTimeZone.UTC), endDate.toDateTimeAtStartOfDay(DateTimeZone.UTC), new BigDecimal("199"));
+        final List<RolledUpUnit> units2 = new ArrayList<RolledUpUnit>();
+        units2.add(createRolledUpUnit("unit", 199L));
+        final RolledUpUsage usage3 = new DefaultRolledUpUsage(subscriptionId, firstBCDDate, endDate, units2);
 
-        final List<RolledUpUsage> usages = ImmutableList.<RolledUpUsage>builder().add(usage1).add(usage2).add(usage3).build();
+        final List<RolledUpUsage> usages = ImmutableList.<RolledUpUsage>builder().add(usage1).add(usage3).build();
         this.mockUsageUserApi = createMockUsageUserApi(usages);
 
         final DefaultTieredBlock block = createDefaultTieredBlock("unit", 100, 10, BigDecimal.ONE);
@@ -181,7 +186,18 @@ public class TestContiguousIntervalConsumableInArrear extends TestUsageInArrearB
         assertEquals(result.get(1).getUsageName(), usage.getName());
         assertTrue(result.get(1).getStartDate().compareTo(firstBCDDate) == 0);
         assertTrue(result.get(1).getEndDate().compareTo(endDate) == 0);
-
     }
 
+    private RolledUpUnit createRolledUpUnit(final String unit, final Long amount) {
+        return new RolledUpUnit() {
+            @Override
+            public String getUnitType() {
+                return unit;
+            }
+            @Override
+            public Long getAmount() {
+                return amount;
+            }
+        };
+    }
 }
diff --git a/invoice/src/test/java/org/killbill/billing/invoice/usage/TestSubscriptionConsumableInArrear.java b/invoice/src/test/java/org/killbill/billing/invoice/usage/TestSubscriptionConsumableInArrear.java
index c124390..9ab1951 100644
--- a/invoice/src/test/java/org/killbill/billing/invoice/usage/TestSubscriptionConsumableInArrear.java
+++ b/invoice/src/test/java/org/killbill/billing/invoice/usage/TestSubscriptionConsumableInArrear.java
@@ -71,7 +71,7 @@ public class TestSubscriptionConsumableInArrear extends TestUsageInArrearBase {
 
         LocalDate targetDate = new LocalDate(2013, 6, 23);
 
-        final SubscriptionConsumableInArrear foo = new SubscriptionConsumableInArrear(invoiceId, billingEvents, usageUserApi, targetDate, callContext);
+        final SubscriptionConsumableInArrear foo = new SubscriptionConsumableInArrear(invoiceId, billingEvents, usageUserApi, true, targetDate, callContext);
         final List<ContiguousIntervalConsumableInArrear> result = foo.computeInArrearUsageInterval();
         assertEquals(result.size(), 3);
 
diff --git a/invoice/src/test/java/org/killbill/billing/invoice/usage/TestUsageInArrearBase.java b/invoice/src/test/java/org/killbill/billing/invoice/usage/TestUsageInArrearBase.java
index 994fac4..6d66b40 100644
--- a/invoice/src/test/java/org/killbill/billing/invoice/usage/TestUsageInArrearBase.java
+++ b/invoice/src/test/java/org/killbill/billing/invoice/usage/TestUsageInArrearBase.java
@@ -78,12 +78,12 @@ public abstract class TestUsageInArrearBase extends InvoiceTestSuiteNoDB {
 
     protected UsageUserApi createMockUsageUserApi(final List<RolledUpUsage> returnValue) {
         final UsageUserApi result = Mockito.mock(UsageUserApi.class);
-        Mockito.when(result.getAllUsageForSubscription(Mockito.<UUID>any(), Mockito.<Set<String>>any(), Mockito.<List<DateTime>>any(), Mockito.<TenantContext>any())).thenReturn(returnValue);
+        Mockito.when(result.getAllUsageForSubscription(Mockito.<UUID>any(), Mockito.<List<LocalDate>>any(), Mockito.<TenantContext>any())).thenReturn(returnValue);
         return result;
     }
 
     protected ContiguousIntervalConsumableInArrear createContiguousIntervalConsumableInArrear(final DefaultUsage usage, final LocalDate targetDate, final boolean closedInterval, final BillingEvent... events) {
-        final ContiguousIntervalConsumableInArrear intervalConsumableInArrear = new ContiguousIntervalConsumableInArrear(usage, invoiceId, mockUsageUserApi, targetDate, callContext);
+        final ContiguousIntervalConsumableInArrear intervalConsumableInArrear = new ContiguousIntervalConsumableInArrear(usage, invoiceId, mockUsageUserApi, true, targetDate, callContext);
         for (BillingEvent event : events) {
             intervalConsumableInArrear.addBillingEvent(event);
         }

jaxrs/pom.xml 7(+6 -1)

diff --git a/jaxrs/pom.xml b/jaxrs/pom.xml
index 1715ed9..7905426 100644
--- a/jaxrs/pom.xml
+++ b/jaxrs/pom.xml
@@ -19,7 +19,7 @@
     <parent>
         <artifactId>killbill</artifactId>
         <groupId>org.kill-bill.billing</groupId>
-        <version>0.11.10-SNAPSHOT</version>
+        <version>0.11.11-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>killbill-jaxrs</artifactId>
@@ -61,6 +61,11 @@
             <scope>provided</scope>
         </dependency>
         <dependency>
+            <!-- Required to bump the com.codahale.metrics:metrics-jersey dependency -->
+            <groupId>com.sun.jersey</groupId>
+            <artifactId>jersey-server</artifactId>
+        </dependency>
+        <dependency>
             <groupId>javax.inject</groupId>
             <artifactId>javax.inject</artifactId>
             <scope>provided</scope>
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/PaymentMethodJson.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/PaymentMethodJson.java
index 7117692..0482484 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/PaymentMethodJson.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/PaymentMethodJson.java
@@ -52,7 +52,7 @@ public class PaymentMethodJson extends JsonBase {
                              @JsonProperty("accountId") final String accountId,
                              @JsonProperty("isDefault") final Boolean isDefault,
                              @JsonProperty("pluginName") final String pluginName,
-                             @JsonProperty("pluginInfo") final PaymentMethodPluginDetailJson pluginInfo,
+                             @JsonProperty("pluginInfo") @Nullable final PaymentMethodPluginDetailJson pluginInfo,
                              @JsonProperty("auditLogs") @Nullable final List<AuditLogJson> auditLogs) {
         super(auditLogs);
         this.externalKey = externalKey;
@@ -135,14 +135,15 @@ public class PaymentMethodJson extends JsonBase {
                         // N/A
                         return false;
                     }
+
                     @Override
                     public String getExternalPaymentMethodId() {
-                        return pluginInfo.getExternalPaymentId();
+                        return pluginInfo != null ? pluginInfo.getExternalPaymentId() : null;
                     }
 
                     @Override
                     public List<PluginProperty> getProperties() {
-                        if (pluginInfo.getProperties() != null) {
+                        if (pluginInfo != null && pluginInfo.getProperties() != null) {
                             final List<PluginProperty> result = new LinkedList<PluginProperty>();
                             for (final PluginPropertyJson cur : pluginInfo.getProperties()) {
                                 result.add(new PluginProperty(cur.getKey(), cur.getValue(), cur.getIsUpdatable()));
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/RolledUpUsageJson.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/RolledUpUsageJson.java
new file mode 100644
index 0000000..98f6812
--- /dev/null
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/RolledUpUsageJson.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright 2014 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.jaxrs.json;
+
+import java.util.List;
+
+import javax.annotation.Nullable;
+
+import org.joda.time.LocalDate;
+import org.killbill.billing.usage.api.RolledUpUnit;
+import org.killbill.billing.usage.api.RolledUpUsage;
+
+import com.apple.jobjc.Invoke.FunCall;
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.google.common.base.Function;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
+
+public class RolledUpUsageJson {
+
+    private final String subscriptionId;
+    private final LocalDate startDate;
+    private final LocalDate endDate;
+    private final List<RolledUpUnitJson> rolledUpUnits;
+
+    @JsonCreator
+    public RolledUpUsageJson(@JsonProperty("subscriptionId") final String subscriptionId,
+                             @JsonProperty("startDate") final LocalDate startDate,
+                             @JsonProperty("endDate") final LocalDate endDate,
+                             @JsonProperty("rolledUpUnit") final List<RolledUpUnitJson> rolledUpUnits) {
+        this.subscriptionId = subscriptionId;
+        this.startDate = startDate;
+        this.endDate = endDate;
+        this.rolledUpUnits = rolledUpUnits;
+    }
+
+    public RolledUpUsageJson(final RolledUpUsage input) {
+        this(input.getSubscriptionId().toString(), input.getStart(), input.getEnd(), ImmutableList.copyOf(Iterables.transform(input.getRolledUpUnit(), new Function<RolledUpUnit, RolledUpUnitJson>() {
+            @Override
+            public RolledUpUnitJson apply(final RolledUpUnit input) {
+                return new RolledUpUnitJson(input);
+            }
+        })));
+    }
+
+    public String getSubscriptionId() {
+        return subscriptionId;
+    }
+
+    public LocalDate getStartDate() {
+        return startDate;
+    }
+
+    public LocalDate getEndDate() {
+        return endDate;
+    }
+
+    public List<RolledUpUnitJson> getRolledUpUnits() {
+        return rolledUpUnits;
+    }
+
+    public static class RolledUpUnitJson {
+
+        private final String unitType;
+        private final Long amount;
+
+        @JsonCreator
+        public RolledUpUnitJson(@JsonProperty("unitType") final String unitType,
+                                @JsonProperty("amount") final Long amount) {
+            this.unitType = unitType;
+            this.amount = amount;
+        }
+
+        public RolledUpUnitJson(final RolledUpUnit input) {
+            this(input.getUnitType(), input.getAmount());
+        }
+
+        public String getUnitType() {
+            return unitType;
+        }
+
+        public Long getAmount() {
+            return amount;
+        }
+    }
+}
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/SubscriptionUsageRecordJson.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/SubscriptionUsageRecordJson.java
new file mode 100644
index 0000000..a7c5cd3
--- /dev/null
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/SubscriptionUsageRecordJson.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright 2014 Groupon, Inc
+ * Copyright 2014 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.jaxrs.json;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.UUID;
+
+import javax.annotation.Nullable;
+
+import org.joda.time.LocalDate;
+import org.killbill.billing.usage.api.SubscriptionUsageRecord;
+import org.killbill.billing.usage.api.UnitUsageRecord;
+import org.killbill.billing.usage.api.UsageRecord;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.google.common.base.Function;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
+
+public class SubscriptionUsageRecordJson {
+
+    private final String subscriptionId;
+    private final List<UnitUsageRecordJson> unitUsageRecords;
+
+    @JsonCreator
+    public SubscriptionUsageRecordJson(@JsonProperty("subscriptionId") final String subscriptionId,
+                                   @JsonProperty("unitUsageRecords") final List<UnitUsageRecordJson> unitUsageRecords) {
+        this.subscriptionId = subscriptionId;
+        this.unitUsageRecords = unitUsageRecords;
+    }
+
+    public String getSubscriptionId() {
+        return subscriptionId;
+    }
+
+    public List<UnitUsageRecordJson> getUnitUsageRecords() {
+        return unitUsageRecords;
+    }
+
+    public static class UnitUsageRecordJson {
+
+        private final String unitType;
+        private final List<UsageRecordJson> usageRecords;
+
+        @JsonCreator
+        public UnitUsageRecordJson(@JsonProperty("unitType") final String unitType,
+                                    @JsonProperty("usageRecords") final List<UsageRecordJson> usageRecords) {
+            this.unitType = unitType;
+            this.usageRecords = usageRecords;
+        }
+
+        public String getUnitType() {
+            return unitType;
+        }
+
+        public List<UsageRecordJson> getUsageRecords() {
+            return usageRecords;
+        }
+
+        public UnitUsageRecord toUnitUsageRecord() {
+            final List<UsageRecord> tmp = ImmutableList.copyOf(Iterables.transform(usageRecords, new Function<UsageRecordJson, UsageRecord>() {
+                @Override
+                public UsageRecord apply(final UsageRecordJson input) {
+                    return input.toUsageRecord();
+                }
+            }));
+            return new UnitUsageRecord(unitType, tmp);
+        }
+    }
+
+    public static class UsageRecordJson {
+
+        private final LocalDate recordDate;
+        private final Long amount;
+
+        @JsonCreator
+        public UsageRecordJson(@JsonProperty("recordDate") final LocalDate recordDate,
+                               @JsonProperty("amount") final Long amount) {
+            this.recordDate = recordDate;
+            this.amount = amount;
+        }
+
+        public LocalDate getRecordDate() {
+            return recordDate;
+        }
+
+        public Long getAmount() {
+            return amount;
+        }
+
+        public UsageRecord toUsageRecord() {
+            return new UsageRecord(recordDate, amount);
+        }
+    }
+
+    public SubscriptionUsageRecord toSubscriptionUsageRecord() {
+        final List<UnitUsageRecord> tmp = ImmutableList.copyOf(Iterables.transform(unitUsageRecords, new Function<UnitUsageRecordJson, UnitUsageRecord>() {
+            @Override
+            public UnitUsageRecord apply(final UnitUsageRecordJson input) {
+                return input.toUnitUsageRecord();
+            }
+        }));
+        final SubscriptionUsageRecord result = new SubscriptionUsageRecord(UUID.fromString(subscriptionId), tmp);
+        return result;
+    }
+}
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/mappers/ExceptionMapperBase.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/mappers/ExceptionMapperBase.java
index 28fde6e..7e32c1d 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/mappers/ExceptionMapperBase.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/mappers/ExceptionMapperBase.java
@@ -21,9 +21,6 @@ import javax.ws.rs.core.Response;
 import javax.ws.rs.core.Response.Status;
 import javax.ws.rs.core.UriInfo;
 
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
 import org.killbill.billing.BillingExceptionBase;
 import org.killbill.billing.account.api.AccountApiException;
 import org.killbill.billing.catalog.api.CatalogApiException;
@@ -41,6 +38,8 @@ import org.killbill.billing.util.api.TagApiException;
 import org.killbill.billing.util.api.TagDefinitionApiException;
 import org.killbill.billing.util.email.EmailApiException;
 import org.killbill.billing.util.jackson.ObjectMapper;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 import com.fasterxml.jackson.core.JsonProcessingException;
 
@@ -113,74 +112,64 @@ public abstract class ExceptionMapperBase {
     protected Response buildConflictingRequestResponse(final Exception e, final UriInfo uriInfo) {
         // Log the full stacktrace
         log.warn("Conflicting request", e);
-        return buildConflictingRequestResponse(exceptionToString(e), uriInfo);
-    }
 
-    private Response buildConflictingRequestResponse(final String error, final UriInfo uriInfo) {
-        return Response.status(Status.CONFLICT)
-                       .entity(error)
-                       .type(MediaType.TEXT_PLAIN_TYPE)
-                       .build();
+        final Response.ResponseBuilder responseBuilder = Response.status(Status.CONFLICT);
+        serializeException(e, responseBuilder);
+        return responseBuilder.build();
     }
 
     protected Response buildNotFoundResponse(final Exception e, final UriInfo uriInfo) {
         // Log the full stacktrace
         log.info("Not found", e);
-        return buildNotFoundResponse(exceptionToString(e), uriInfo);
-    }
 
-    private Response buildNotFoundResponse(final String error, final UriInfo uriInfo) {
-        return Response.status(Status.NOT_FOUND)
-                       .entity(error)
-                       .type(MediaType.TEXT_PLAIN_TYPE)
-                       .build();
+        final Response.ResponseBuilder responseBuilder = Response.status(Status.NOT_FOUND);
+        serializeException(e, responseBuilder);
+        return responseBuilder.build();
     }
 
     protected Response buildBadRequestResponse(final Exception e, final UriInfo uriInfo) {
         // Log the full stacktrace
         log.warn("Bad request", e);
-        return buildBadRequestResponse(exceptionToString(e), uriInfo);
-    }
 
-    private Response buildBadRequestResponse(final String error, final UriInfo uriInfo) {
-        return Response.status(Status.BAD_REQUEST)
-                       .entity(error)
-                       .type(MediaType.TEXT_PLAIN_TYPE)
-                       .build();
+        final Response.ResponseBuilder responseBuilder = Response.status(Status.BAD_REQUEST);
+        serializeException(e, responseBuilder);
+        return responseBuilder.build();
     }
 
     protected Response buildAuthorizationErrorResponse(final Exception e, final UriInfo uriInfo) {
         // Log the full stacktrace
         log.warn("Authorization error", e);
-        return buildAuthorizationErrorResponse(exceptionToString(e), uriInfo);
-    }
 
-    private Response buildAuthorizationErrorResponse(final String error, final UriInfo uriInfo) {
-        return Response.status(Status.UNAUTHORIZED) // TODO Forbidden?
-                       .entity(error)
-                       .type(MediaType.TEXT_PLAIN_TYPE)
-                       .build();
+        // TODO Forbidden?
+        final Response.ResponseBuilder responseBuilder = Response.status(Status.UNAUTHORIZED);
+        serializeException(e, responseBuilder);
+        return responseBuilder.build();
     }
 
     protected Response buildInternalErrorResponse(final Exception e, final UriInfo uriInfo) {
         // Log the full stacktrace
         log.warn("Internal error", e);
-        return buildInternalErrorResponse(exceptionToString(e), uriInfo);
+
+        final Response.ResponseBuilder responseBuilder = Response.status(Status.INTERNAL_SERVER_ERROR);
+        serializeException(e, responseBuilder);
+        return responseBuilder.build();
     }
 
-    private Response buildInternalErrorResponse(final String error, final UriInfo uriInfo) {
-        return Response.status(Status.INTERNAL_SERVER_ERROR)
-                       .entity(error)
-                       .type(MediaType.TEXT_PLAIN_TYPE)
-                       .build();
+    protected Response buildPluginTimeoutResponse(final Exception e, final UriInfo uriInfo) {
+        final Response.ResponseBuilder responseBuilder = Response.status(Status.ACCEPTED);
+        serializeException(e, responseBuilder);
+        return responseBuilder.build();
     }
 
-    private String exceptionToString(final Exception e) {
+    private void serializeException(final Exception e, final Response.ResponseBuilder responseBuilder) {
+        final BillingExceptionJson billingExceptionJson = new BillingExceptionJson(e);
+
         try {
-            return mapper.writeValueAsString(new BillingExceptionJson(e));
-        } catch (final JsonProcessingException jsonException) {
+            final String billingExceptionJsonAsString = mapper.writeValueAsString(billingExceptionJson);
+            responseBuilder.entity(billingExceptionJsonAsString).type(MediaType.APPLICATION_JSON);
+        } catch (JsonProcessingException jsonException) {
             log.warn("Unable to serialize exception", jsonException);
+            responseBuilder.entity(e.toString()).type(MediaType.TEXT_PLAIN_TYPE);
         }
-        return e.toString();
     }
 }
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/mappers/PaymentApiExceptionMapper.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/mappers/PaymentApiExceptionMapper.java
index 9fb84ad..c89369d 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/mappers/PaymentApiExceptionMapper.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/mappers/PaymentApiExceptionMapper.java
@@ -63,7 +63,7 @@ public class PaymentApiExceptionMapper extends ExceptionMapperBase implements Ex
         } else if (exception.getCode() == ErrorCode.PAYMENT_NULL_INVOICE.getCode()) {
             return buildBadRequestResponse(exception, uriInfo);
         } else if (exception.getCode() == ErrorCode.PAYMENT_PLUGIN_TIMEOUT.getCode()) {
-            return buildInternalErrorResponse(exception, uriInfo);
+            return buildPluginTimeoutResponse(exception, uriInfo);
         } else if (exception.getCode() == ErrorCode.PAYMENT_REFRESH_PAYMENT_METHOD.getCode()) {
             return buildInternalErrorResponse(exception, uriInfo);
         } else if (exception.getCode() == ErrorCode.PAYMENT_UPD_PAYMENT_METHOD.getCode()) {
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/AccountResource.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/AccountResource.java
index 56a49c8..95e1a82 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/AccountResource.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/AccountResource.java
@@ -141,7 +141,7 @@ public class AccountResource extends JaxRsResourceBase {
         this.overdueApi = overdueApi;
     }
 
-    //@Timed
+    @Timed
     @GET
     @Path("/{accountId:" + UUID_PATTERN + "}")
     @Produces(APPLICATION_JSON)
@@ -157,7 +157,7 @@ public class AccountResource extends JaxRsResourceBase {
         return Response.status(Status.OK).entity(accountJson).build();
     }
 
-    //@Timed
+    @Timed
     @GET
     @Path("/" + PAGINATION)
     @Produces(APPLICATION_JSON)
@@ -184,7 +184,7 @@ public class AccountResource extends JaxRsResourceBase {
                                                );
     }
 
-    //@Timed
+    @Timed
     @GET
     @Path("/" + SEARCH + "/{searchKey:" + ANYTHING_PATTERN + "}")
     @Produces(APPLICATION_JSON)
@@ -213,7 +213,7 @@ public class AccountResource extends JaxRsResourceBase {
                                                );
     }
 
-    //@Timed
+    @Timed
     @GET
     @Path("/{accountId:" + UUID_PATTERN + "}/" + BUNDLES)
     @Produces(APPLICATION_JSON)
@@ -238,7 +238,7 @@ public class AccountResource extends JaxRsResourceBase {
         return Response.status(Status.OK).entity(result).build();
     }
 
-    //@Timed
+    @Timed
     @GET
     @Produces(APPLICATION_JSON)
     public Response getAccountByKey(@QueryParam(QUERY_EXTERNAL_KEY) final String externalKey,
@@ -267,7 +267,7 @@ public class AccountResource extends JaxRsResourceBase {
         }
     }
 
-    //@Timed
+    @Timed
     @POST
     @Consumes(APPLICATION_JSON)
     @Produces(APPLICATION_JSON)
@@ -282,7 +282,7 @@ public class AccountResource extends JaxRsResourceBase {
         return uriBuilder.buildResponse(uriInfo, AccountResource.class, "getAccount", account.getId());
     }
 
-    //@Timed
+    @Timed
     @PUT
     @Consumes(APPLICATION_JSON)
     @Produces(APPLICATION_JSON)
@@ -300,7 +300,7 @@ public class AccountResource extends JaxRsResourceBase {
     }
 
     // Not supported
-    //@Timed
+    @Timed
     @DELETE
     @Path("/{accountId:" + UUID_PATTERN + "}")
     @Produces(APPLICATION_JSON)
@@ -318,7 +318,7 @@ public class AccountResource extends JaxRsResourceBase {
         return Response.status(Status.INTERNAL_SERVER_ERROR).build();
     }
 
-    //@Timed
+    @Timed
     @GET
     @Path("/{accountId:" + UUID_PATTERN + "}/" + TIMELINE)
     @Produces(APPLICATION_JSON)
@@ -352,7 +352,7 @@ public class AccountResource extends JaxRsResourceBase {
     * ************************** EMAIL NOTIFICATIONS FOR INVOICES ********************************
     */
 
-    //@Timed
+    @Timed
     @GET
     @Path("/{accountId:" + UUID_PATTERN + "}/" + EMAIL_NOTIFICATIONS)
     @Produces(APPLICATION_JSON)
@@ -364,7 +364,7 @@ public class AccountResource extends JaxRsResourceBase {
         return Response.status(Status.OK).entity(invoiceEmailJson).build();
     }
 
-    //@Timed
+    @Timed
     @PUT
     @Path("/{accountId:" + UUID_PATTERN + "}/" + EMAIL_NOTIFICATIONS)
     @Consumes(APPLICATION_JSON)
@@ -390,7 +390,7 @@ public class AccountResource extends JaxRsResourceBase {
     /*
      * ************************** INVOICE CBA REBALANCING ********************************
      */
-    //@Timed
+    @Timed
     @POST
     @Path("/{accountId:" + UUID_PATTERN + "}/" + CBA_REBALANCING)
     @Consumes(APPLICATION_JSON)
@@ -413,7 +413,7 @@ public class AccountResource extends JaxRsResourceBase {
      * ************************** INVOICES ********************************
      */
 
-    //@Timed
+    @Timed
     @GET
     @Path("/{accountId:" + UUID_PATTERN + "}/" + INVOICES)
     @Produces(APPLICATION_JSON)
@@ -447,7 +447,7 @@ public class AccountResource extends JaxRsResourceBase {
      */
 
     // STEPH should refactor code since very similar to @Path("/{accountId:" + UUID_PATTERN + "}/" + PAYMENTS)
-    //@Timed
+    @Timed
     @GET
     @Path("/{accountId:" + UUID_PATTERN + "}/" + INVOICE_PAYMENTS)
     @Produces(APPLICATION_JSON)
@@ -471,7 +471,7 @@ public class AccountResource extends JaxRsResourceBase {
         return Response.status(Status.OK).entity(result).build();
     }
 
-    //@Timed
+    @Timed
     @POST
     @Produces(APPLICATION_JSON)
     @Consumes(APPLICATION_JSON)
@@ -519,7 +519,7 @@ public class AccountResource extends JaxRsResourceBase {
         return Response.status(Status.OK).build();
     }
 
-    //@Timed
+    @Timed
     @POST
     @Path("/{accountId:" + UUID_PATTERN + "}/" + PAYMENT_METHODS)
     @Consumes(APPLICATION_JSON)
@@ -556,7 +556,7 @@ public class AccountResource extends JaxRsResourceBase {
         return uriBuilder.buildResponse(PaymentMethodResource.class, "getPaymentMethod", paymentMethodId, uriInfo.getBaseUri().toString());
     }
 
-    //@Timed
+    @Timed
     @GET
     @Path("/{accountId:" + UUID_PATTERN + "}/" + PAYMENT_METHODS)
     @Produces(APPLICATION_JSON)
@@ -581,7 +581,7 @@ public class AccountResource extends JaxRsResourceBase {
         return Response.status(Status.OK).entity(json).build();
     }
 
-    //@Timed
+    @Timed
     @PUT
     @Consumes(APPLICATION_JSON)
     @Produces(APPLICATION_JSON)
@@ -612,7 +612,7 @@ public class AccountResource extends JaxRsResourceBase {
     /*
      * ************************* PAYMENTS *****************************
      */
-    //@Timed
+    @Timed
     @GET
     @Path("/{accountId:" + UUID_PATTERN + "}/" + PAYMENTS)
     @Produces(APPLICATION_JSON)
@@ -635,7 +635,7 @@ public class AccountResource extends JaxRsResourceBase {
         return Response.status(Response.Status.OK).entity(result).build();
     }
 
-    //@Timed
+    @Timed
     @POST
     @Path("/{accountId:" + UUID_PATTERN + "}/" + PAYMENTS)
     @Consumes(APPLICATION_JSON)
@@ -684,7 +684,7 @@ public class AccountResource extends JaxRsResourceBase {
     /*
      * ************************** OVERDUE ********************************
      */
-    //@Timed
+    @Timed
     @GET
     @Path("/{accountId:" + UUID_PATTERN + "}/" + OVERDUE)
     @Produces(APPLICATION_JSON)
@@ -702,7 +702,7 @@ public class AccountResource extends JaxRsResourceBase {
      * *************************      CUSTOM FIELDS     *****************************
      */
 
-    //@Timed
+    @Timed
     @GET
     @Path("/{accountId:" + UUID_PATTERN + "}/" + CUSTOM_FIELDS)
     @Produces(APPLICATION_JSON)
@@ -712,7 +712,7 @@ public class AccountResource extends JaxRsResourceBase {
         return super.getCustomFields(UUID.fromString(id), auditMode, context.createContext(request));
     }
 
-    //@Timed
+    @Timed
     @POST
     @Path("/{accountId:" + UUID_PATTERN + "}/" + CUSTOM_FIELDS)
     @Consumes(APPLICATION_JSON)
@@ -728,7 +728,7 @@ public class AccountResource extends JaxRsResourceBase {
                                         context.createContext(createdBy, reason, comment, request), uriInfo);
     }
 
-    //@Timed
+    @Timed
     @DELETE
     @Path("/{accountId:" + UUID_PATTERN + "}/" + CUSTOM_FIELDS)
     @Consumes(APPLICATION_JSON)
@@ -747,7 +747,7 @@ public class AccountResource extends JaxRsResourceBase {
      * *************************     TAGS     *****************************
      */
 
-    //@Timed
+    @Timed
     @GET
     @Path("/{accountId:" + UUID_PATTERN + "}/" + TAGS)
     @Produces(APPLICATION_JSON)
@@ -759,7 +759,7 @@ public class AccountResource extends JaxRsResourceBase {
         return super.getTags(accountId, accountId, auditMode, includedDeleted, context.createContext(request));
     }
 
-    //@Timed
+    @Timed
     @POST
     @Path("/{accountId:" + UUID_PATTERN + "}/" + TAGS)
     @Produces(APPLICATION_JSON)
@@ -774,7 +774,7 @@ public class AccountResource extends JaxRsResourceBase {
                                 context.createContext(createdBy, reason, comment, request));
     }
 
-    //@Timed
+    @Timed
     @DELETE
     @Path("/{accountId:" + UUID_PATTERN + "}/" + TAGS)
     @Consumes(APPLICATION_JSON)
@@ -812,7 +812,7 @@ public class AccountResource extends JaxRsResourceBase {
      * *************************     EMAILS     *****************************
      */
 
-    //@Timed
+    @Timed
     @GET
     @Path("/{accountId:" + UUID_PATTERN + "}/" + EMAILS)
     @Produces(APPLICATION_JSON)
@@ -828,7 +828,7 @@ public class AccountResource extends JaxRsResourceBase {
         return Response.status(Status.OK).entity(emailsJson).build();
     }
 
-    //@Timed
+    @Timed
     @POST
     @Path("/{accountId:" + UUID_PATTERN + "}/" + EMAILS)
     @Consumes(APPLICATION_JSON)
@@ -864,7 +864,7 @@ public class AccountResource extends JaxRsResourceBase {
         return uriBuilder.buildResponse(uriInfo, AccountResource.class, "getEmails", json.getAccountId());
     }
 
-    //@Timed
+    @Timed
     @DELETE
     @Path("/{accountId:" + UUID_PATTERN + "}/" + EMAILS + "/{email}")
     @Produces(APPLICATION_JSON)
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/BundleResource.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/BundleResource.java
index 2ca083c..ad97126 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/BundleResource.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/BundleResource.java
@@ -98,7 +98,7 @@ public class BundleResource extends JaxRsResourceBase {
         this.subscriptionApi = subscriptionApi;
     }
 
-    //@Timed
+    @Timed
     @GET
     @Path("/{bundleId:" + UUID_PATTERN + "}")
     @Produces(APPLICATION_JSON)
@@ -110,7 +110,7 @@ public class BundleResource extends JaxRsResourceBase {
         return Response.status(Status.OK).entity(json).build();
     }
 
-    //@Timed
+    @Timed
     @GET
     @Produces(APPLICATION_JSON)
     public Response getBundleByKey(@QueryParam(QUERY_EXTERNAL_KEY) final String externalKey,
@@ -120,7 +120,7 @@ public class BundleResource extends JaxRsResourceBase {
         return Response.status(Status.OK).entity(json).build();
     }
 
-    //@Timed
+    @Timed
     @GET
     @Path("/" + PAGINATION)
     @Produces(APPLICATION_JSON)
@@ -146,7 +146,7 @@ public class BundleResource extends JaxRsResourceBase {
                                                 nextPageUri);
     }
 
-    //@Timed
+    @Timed
     @GET
     @Path("/" + SEARCH + "/{searchKey:" + ANYTHING_PATTERN + "}")
     @Produces(APPLICATION_JSON)
@@ -174,7 +174,7 @@ public class BundleResource extends JaxRsResourceBase {
                                                 nextPageUri);
     }
 
-    //@Timed
+    @Timed
     @PUT
     @Path("/{bundleId:" + UUID_PATTERN + "}/" + PAUSE)
     @Consumes(APPLICATION_JSON)
@@ -194,7 +194,7 @@ public class BundleResource extends JaxRsResourceBase {
         return Response.status(Status.OK).build();
     }
 
-    //@Timed
+    @Timed
     @PUT
     @Path("/{bundleId:" + UUID_PATTERN + "}/" + RESUME)
     @Consumes(APPLICATION_JSON)
@@ -214,7 +214,7 @@ public class BundleResource extends JaxRsResourceBase {
         return Response.status(Status.OK).build();
     }
 
-    //@Timed
+    @Timed
     @GET
     @Path("/{bundleId:" + UUID_PATTERN + "}/" + CUSTOM_FIELDS)
     @Produces(APPLICATION_JSON)
@@ -224,7 +224,7 @@ public class BundleResource extends JaxRsResourceBase {
         return super.getCustomFields(UUID.fromString(id), auditMode, context.createContext(request));
     }
 
-    //@Timed
+    @Timed
     @POST
     @Path("/{bundleId:" + UUID_PATTERN + "}/" + CUSTOM_FIELDS)
     @Consumes(APPLICATION_JSON)
@@ -240,7 +240,7 @@ public class BundleResource extends JaxRsResourceBase {
                                         context.createContext(createdBy, reason, comment, request), uriInfo);
     }
 
-    //@Timed
+    @Timed
     @DELETE
     @Path("/{bundleId:" + UUID_PATTERN + "}/" + CUSTOM_FIELDS)
     @Consumes(APPLICATION_JSON)
@@ -255,7 +255,7 @@ public class BundleResource extends JaxRsResourceBase {
                                         context.createContext(createdBy, reason, comment, request));
     }
 
-    //@Timed
+    @Timed
     @GET
     @Path("/{bundleId:" + UUID_PATTERN + "}/" + TAGS)
     @Produces(APPLICATION_JSON)
@@ -269,7 +269,7 @@ public class BundleResource extends JaxRsResourceBase {
         return super.getTags(bundle.getAccountId(), bundleId, auditMode, includedDeleted, tenantContext);
     }
 
-    //@Timed
+    @Timed
     @PUT
     @Path("/{bundleId:" + UUID_PATTERN + "}")
     @Consumes(APPLICATION_JSON)
@@ -296,7 +296,7 @@ public class BundleResource extends JaxRsResourceBase {
         return uriBuilder.buildResponse(BundleResource.class, "getBundle", newBundleId, uriInfo.getBaseUri().toString());
     }
 
-    //@Timed
+    @Timed
     @POST
     @Path("/{bundleId:" + UUID_PATTERN + "}/" + TAGS)
     @Consumes(APPLICATION_JSON)
@@ -312,7 +312,7 @@ public class BundleResource extends JaxRsResourceBase {
                                 context.createContext(createdBy, reason, comment, request));
     }
 
-    //@Timed
+    @Timed
     @DELETE
     @Path("/{bundleId:" + UUID_PATTERN + "}/" + TAGS)
     @Consumes(APPLICATION_JSON)
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/CatalogResource.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/CatalogResource.java
index ad882fc..1de293d 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/CatalogResource.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/CatalogResource.java
@@ -70,14 +70,14 @@ public class CatalogResource extends JaxRsResourceBase {
         this.catalogService = catalogService;
     }
 
-    //@Timed
+    @Timed
     @GET
     @Produces(APPLICATION_XML)
     public Response getCatalogXml(@javax.ws.rs.core.Context final HttpServletRequest request) throws Exception {
         return Response.status(Status.OK).entity(XMLWriter.writeXML(catalogService.getCurrentCatalog(), StaticCatalog.class)).build();
     }
 
-    //@Timed
+    @Timed
     @GET
     @Produces(APPLICATION_JSON)
     public Response getCatalogJson(@javax.ws.rs.core.Context final HttpServletRequest request) throws Exception {
@@ -100,7 +100,7 @@ public class CatalogResource extends JaxRsResourceBase {
     //        return result;
     //    }
 
-    //@Timed
+    @Timed
     @GET
     @Path("/availableAddons")
     @Produces(APPLICATION_JSON)
@@ -115,7 +115,7 @@ public class CatalogResource extends JaxRsResourceBase {
         return Response.status(Status.OK).entity(details).build();
     }
 
-    //@Timed
+    @Timed
     @GET
     @Path("/availableBasePlans")
     @Produces(APPLICATION_JSON)
@@ -129,7 +129,7 @@ public class CatalogResource extends JaxRsResourceBase {
         return Response.status(Status.OK).entity(details).build();
     }
 
-    //@Timed
+    @Timed
     @GET
     @Path("/simpleCatalog")
     @Produces(APPLICATION_JSON)
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/CustomFieldResource.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/CustomFieldResource.java
index a4895eb..35556ad 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/CustomFieldResource.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/CustomFieldResource.java
@@ -68,7 +68,7 @@ public class CustomFieldResource extends JaxRsResourceBase {
         super(uriBuilder, tagUserApi, customFieldUserApi, auditUserApi, accountUserApi, paymentApi, clock, context);
     }
 
-    //@Timed
+    @Timed
     @GET
     @Path("/" + PAGINATION)
     @Produces(APPLICATION_JSON)
@@ -92,7 +92,7 @@ public class CustomFieldResource extends JaxRsResourceBase {
                                                 nextPageUri);
     }
 
-    //@Timed
+    @Timed
     @GET
     @Path("/" + SEARCH + "/{searchKey:" + ANYTHING_PATTERN + "}")
     @Produces(APPLICATION_JSON)
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/ExportResource.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/ExportResource.java
index cf4aa7b..672d7e2 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/ExportResource.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/ExportResource.java
@@ -66,7 +66,7 @@ public class ExportResource extends JaxRsResourceBase {
         this.exportUserApi = exportUserApi;
     }
 
-    //@Timed
+    @Timed
     @GET
     @Path("/{accountId:" + UUID_PATTERN + "}")
     @Produces(TEXT_PLAIN)
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/InvoicePaymentResource.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/InvoicePaymentResource.java
index 2ddeed1..2995a0c 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/InvoicePaymentResource.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/InvoicePaymentResource.java
@@ -95,7 +95,7 @@ public class InvoicePaymentResource extends JaxRsResourceBase {
         this.invoicePaymentApi = invoicePaymentApi;
     }
 
-    //@Timed
+    @Timed
     @GET
     @Path("/{paymentId:" + UUID_PATTERN + "}/")
     @Produces(APPLICATION_JSON)
@@ -124,7 +124,7 @@ public class InvoicePaymentResource extends JaxRsResourceBase {
         return Response.status(Response.Status.OK).entity(result).build();
     }
 
-    //@Timed
+    @Timed
     @POST
     @Path("/{paymentId:" + UUID_PATTERN + "}/" + REFUNDS)
     @Consumes(APPLICATION_JSON)
@@ -167,7 +167,7 @@ public class InvoicePaymentResource extends JaxRsResourceBase {
         return uriBuilder.buildResponse(InvoicePaymentResource.class, "getInvoicePayment", result.getId(), uriInfo.getBaseUri().toString());
     }
 
-    //@Timed
+    @Timed
     @POST
     @Path("/{paymentId:" + UUID_PATTERN + "}/" + CHARGEBACKS)
     @Consumes(APPLICATION_JSON)
@@ -191,7 +191,7 @@ public class InvoicePaymentResource extends JaxRsResourceBase {
         return uriBuilder.buildResponse(uriInfo, InvoicePaymentResource.class, "getInvoicePayment", result.getId());
     }
 
-    //@Timed
+    @Timed
     @GET
     @Path("/{paymentId:" + UUID_PATTERN + "}/" + CUSTOM_FIELDS)
     @Produces(APPLICATION_JSON)
@@ -201,7 +201,7 @@ public class InvoicePaymentResource extends JaxRsResourceBase {
         return super.getCustomFields(UUID.fromString(id), auditMode, context.createContext(request));
     }
 
-    //@Timed
+    @Timed
     @POST
     @Path("/{paymentId:" + UUID_PATTERN + "}/" + CUSTOM_FIELDS)
     @Consumes(APPLICATION_JSON)
@@ -217,7 +217,7 @@ public class InvoicePaymentResource extends JaxRsResourceBase {
                                         context.createContext(createdBy, reason, comment, request), uriInfo);
     }
 
-    //@Timed
+    @Timed
     @DELETE
     @Path("/{paymentId:" + UUID_PATTERN + "}/" + CUSTOM_FIELDS)
     @Consumes(APPLICATION_JSON)
@@ -232,7 +232,7 @@ public class InvoicePaymentResource extends JaxRsResourceBase {
                                         context.createContext(createdBy, reason, comment, request));
     }
 
-    //@Timed
+    @Timed
     @GET
     @Path("/{paymentId:" + UUID_PATTERN + "}/" + TAGS)
     @Produces(APPLICATION_JSON)
@@ -248,7 +248,7 @@ public class InvoicePaymentResource extends JaxRsResourceBase {
         return super.getTags(payment.getAccountId(), paymentId, auditMode, includedDeleted, tenantContext);
     }
 
-    //@Timed
+    @Timed
     @POST
     @Path("/{paymentId:" + UUID_PATTERN + "}/" + TAGS)
     @Consumes(APPLICATION_JSON)
@@ -264,7 +264,7 @@ public class InvoicePaymentResource extends JaxRsResourceBase {
                                 context.createContext(createdBy, reason, comment, request));
     }
 
-    //@Timed
+    @Timed
     @DELETE
     @Path("/{paymentId:" + UUID_PATTERN + "}/" + TAGS)
     @Consumes(APPLICATION_JSON)
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/InvoiceResource.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/InvoiceResource.java
index ad2e32a..43ab896 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/InvoiceResource.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/InvoiceResource.java
@@ -119,7 +119,7 @@ public class InvoiceResource extends JaxRsResourceBase {
         this.invoiceNotifier = invoiceNotifier;
     }
 
-    //@Timed
+    @Timed
     @GET
     @Path("/{invoiceId:" + UUID_PATTERN + "}/")
     @Produces(APPLICATION_JSON)
@@ -139,7 +139,7 @@ public class InvoiceResource extends JaxRsResourceBase {
         }
     }
 
-    //@Timed
+    @Timed
     @GET
     @Path("/{invoiceNumber:" + NUMBER_PATTERN + "}/")
     @Produces(APPLICATION_JSON)
@@ -159,7 +159,7 @@ public class InvoiceResource extends JaxRsResourceBase {
         }
     }
 
-    //@Timed
+    @Timed
     @GET
     @Path("/{invoiceId:" + UUID_PATTERN + "}/html")
     @Produces(TEXT_HTML)
@@ -168,7 +168,7 @@ public class InvoiceResource extends JaxRsResourceBase {
         return Response.status(Status.OK).entity(invoiceApi.getInvoiceAsHTML(UUID.fromString(invoiceId), context.createContext(request))).build();
     }
 
-    //@Timed
+    @Timed
     @GET
     @Path("/" + PAGINATION)
     @Produces(APPLICATION_JSON)
@@ -198,7 +198,7 @@ public class InvoiceResource extends JaxRsResourceBase {
                                                );
     }
 
-    //@Timed
+    @Timed
     @GET
     @Path("/" + SEARCH + "/{searchKey:" + ANYTHING_PATTERN + "}")
     @Produces(APPLICATION_JSON)
@@ -229,7 +229,7 @@ public class InvoiceResource extends JaxRsResourceBase {
                                                );
     }
 
-    //@Timed
+    @Timed
     @POST
     @Consumes(APPLICATION_JSON)
     @Produces(APPLICATION_JSON)
@@ -253,7 +253,7 @@ public class InvoiceResource extends JaxRsResourceBase {
         }
     }
 
-    //@Timed
+    @Timed
     @DELETE
     @Path("/{invoiceId:" + UUID_PATTERN + "}" + "/{invoiceItemId:" + UUID_PATTERN + "}/cba")
     @Consumes(APPLICATION_JSON)
@@ -274,7 +274,7 @@ public class InvoiceResource extends JaxRsResourceBase {
         return Response.status(Status.OK).build();
     }
 
-    //@Timed
+    @Timed
     @POST
     @Path("/{invoiceId:" + UUID_PATTERN + "}")
     @Consumes(APPLICATION_JSON)
@@ -311,7 +311,7 @@ public class InvoiceResource extends JaxRsResourceBase {
         return uriBuilder.buildResponse(uriInfo, InvoiceResource.class, "getInvoice", adjustmentItem.getInvoiceId());
     }
 
-    //@Timed
+    @Timed
     @POST
     @Produces(APPLICATION_JSON)
     @Consumes(APPLICATION_JSON)
@@ -380,7 +380,7 @@ public class InvoiceResource extends JaxRsResourceBase {
         return Response.status(Status.OK).entity(createdExternalChargesJson).build();
     }
 
-    //@Timed
+    @Timed
     @GET
     @Path("/{invoiceId:" + UUID_PATTERN + "}/" + PAYMENTS)
     @Produces(APPLICATION_JSON)
@@ -406,7 +406,7 @@ public class InvoiceResource extends JaxRsResourceBase {
         return Response.status(Status.OK).entity(result).build();
     }
 
-    //@Timed
+    @Timed
     @POST
     @Produces(APPLICATION_JSON)
     @Consumes(APPLICATION_JSON)
@@ -429,7 +429,7 @@ public class InvoiceResource extends JaxRsResourceBase {
         return uriBuilder.buildResponse(uriInfo, InvoicePaymentResource.class, "getInvoicePayment", result.getId());
     }
 
-    //@Timed
+    @Timed
     @POST
     @Path("/{invoiceId:" + UUID_PATTERN + "}/" + EMAIL_NOTIFICATIONS)
     @Consumes(APPLICATION_JSON)
@@ -454,7 +454,7 @@ public class InvoiceResource extends JaxRsResourceBase {
         return Response.status(Status.OK).build();
     }
 
-    //@Timed
+    @Timed
     @GET
     @Path("/{invoiceId:" + UUID_PATTERN + "}/" + CUSTOM_FIELDS)
     @Produces(APPLICATION_JSON)
@@ -464,7 +464,7 @@ public class InvoiceResource extends JaxRsResourceBase {
         return super.getCustomFields(UUID.fromString(id), auditMode, context.createContext(request));
     }
 
-    //@Timed
+    @Timed
     @POST
     @Path("/{invoiceId:" + UUID_PATTERN + "}/" + CUSTOM_FIELDS)
     @Consumes(APPLICATION_JSON)
@@ -480,7 +480,7 @@ public class InvoiceResource extends JaxRsResourceBase {
                                         context.createContext(createdBy, reason, comment, request), uriInfo);
     }
 
-    //@Timed
+    @Timed
     @DELETE
     @Path("/{invoiceId:" + UUID_PATTERN + "}/" + CUSTOM_FIELDS)
     @Consumes(APPLICATION_JSON)
@@ -495,7 +495,7 @@ public class InvoiceResource extends JaxRsResourceBase {
                                         context.createContext(createdBy, reason, comment, request));
     }
 
-    //@Timed
+    @Timed
     @GET
     @Path("/{invoiceId:" + UUID_PATTERN + "}/" + TAGS)
     @Produces(APPLICATION_JSON)
@@ -509,7 +509,7 @@ public class InvoiceResource extends JaxRsResourceBase {
         return super.getTags(invoice.getAccountId(), invoiceId, auditMode, includedDeleted, tenantContext);
     }
 
-    //@Timed
+    @Timed
     @POST
     @Path("/{invoiceId:" + UUID_PATTERN + "}/" + TAGS)
     @Consumes(APPLICATION_JSON)
@@ -525,7 +525,7 @@ public class InvoiceResource extends JaxRsResourceBase {
                                 context.createContext(createdBy, reason, comment, request));
     }
 
-    //@Timed
+    @Timed
     @DELETE
     @Path("/{invoiceId:" + UUID_PATTERN + "}/" + TAGS)
     @Consumes(APPLICATION_JSON)
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/JaxrsResource.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/JaxrsResource.java
index 5fa2d62..aeabae3 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/JaxrsResource.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/JaxrsResource.java
@@ -98,8 +98,8 @@ public interface JaxrsResource {
 
     public static final String QUERY_PLUGIN_PROPERTY = "pluginProperty";
 
-    public static final String QUERY_START_TIME = "startTime";
-    public static final String QUERY_END_TIME = "endTime";
+    public static final String QUERY_START_DATE = "startDate";
+    public static final String QUERY_END_DATE = "endDate";
 
     public static final String QUERY_BUNDLE_TRANSFER_ADDON = "transferAddOn";
     public static final String QUERY_BUNDLE_TRANSFER_CANCEL_IMM = "cancelImmediately";
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/PaymentGatewayResource.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/PaymentGatewayResource.java
index 7282bce..829bef6 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/PaymentGatewayResource.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/PaymentGatewayResource.java
@@ -81,7 +81,7 @@ public class PaymentGatewayResource extends JaxRsResourceBase {
         this.paymentGatewayApi = paymentGatewayApi;
     }
 
-    //@Timed
+    @Timed
     @POST
     @Path("/" + HOSTED + "/" + FORM + "/{" + QUERY_ACCOUNT_ID + ":" + UUID_PATTERN + "}")
     @Consumes(APPLICATION_JSON)
@@ -119,7 +119,7 @@ public class PaymentGatewayResource extends JaxRsResourceBase {
         return Response.status(Response.Status.OK).entity(result).build();
     }
 
-    //@Timed
+    @Timed
     @POST
     @Path("/" + NOTIFICATION + "/{" + QUERY_PAYMENT_PLUGIN_NAME + ":" + ANYTHING_PATTERN + "}")
     @Consumes(WILDCARD)
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/PaymentMethodResource.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/PaymentMethodResource.java
index 6f6a597..183eb21 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/PaymentMethodResource.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/PaymentMethodResource.java
@@ -82,7 +82,7 @@ public class PaymentMethodResource extends JaxRsResourceBase {
         super(uriBuilder, tagUserApi, customFieldUserApi, auditUserApi, accountUserApi, paymentApi, clock, context);
     }
 
-    //@Timed
+    @Timed
     @GET
     @Path("/{paymentMethodId:" + UUID_PATTERN + "}")
     @Produces(APPLICATION_JSON)
@@ -102,7 +102,7 @@ public class PaymentMethodResource extends JaxRsResourceBase {
         return Response.status(Status.OK).entity(json).build();
     }
 
-    //@Timed
+    @Timed
     @GET
     @Produces(APPLICATION_JSON)
     public Response getPaymentMethodByKey(@QueryParam(QUERY_EXTERNAL_KEY) final String externalKey,
@@ -120,7 +120,7 @@ public class PaymentMethodResource extends JaxRsResourceBase {
         return Response.status(Status.OK).entity(json).build();
     }
 
-    //@Timed
+    @Timed
     @GET
     @Path("/" + PAGINATION)
     @Produces(APPLICATION_JSON)
@@ -174,7 +174,7 @@ public class PaymentMethodResource extends JaxRsResourceBase {
                                                );
     }
 
-    //@Timed
+    @Timed
     @GET
     @Path("/" + SEARCH + "/{searchKey:" + ANYTHING_PATTERN + "}")
     @Produces(APPLICATION_JSON)
@@ -231,7 +231,7 @@ public class PaymentMethodResource extends JaxRsResourceBase {
                                                );
     }
 
-    //@Timed
+    @Timed
     @DELETE
     @Produces(APPLICATION_JSON)
     @Path("/{paymentMethodId:" + UUID_PATTERN + "}")
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/PaymentResource.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/PaymentResource.java
index 2ccf734..b9496c4 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/PaymentResource.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/PaymentResource.java
@@ -83,7 +83,7 @@ public class PaymentResource extends JaxRsResourceBase {
         super(uriBuilder, tagUserApi, customFieldUserApi, auditUserApi, accountUserApi, paymentApi, clock, context);
     }
 
-    //@Timed
+    @Timed
     @GET
     @Path("/{paymentId:" + UUID_PATTERN + "}/")
     @Produces(APPLICATION_JSON)
@@ -102,7 +102,7 @@ public class PaymentResource extends JaxRsResourceBase {
         return Response.status(Response.Status.OK).entity(result).build();
     }
 
-    //@Timed
+    @Timed
     @GET
     @Path("/" + PAGINATION)
     @Produces(APPLICATION_JSON)
@@ -143,7 +143,7 @@ public class PaymentResource extends JaxRsResourceBase {
                                                );
     }
 
-    //@Timed
+    @Timed
     @GET
     @Path("/" + SEARCH + "/{searchKey:" + ANYTHING_PATTERN + "}")
     @Produces(APPLICATION_JSON)
@@ -187,7 +187,7 @@ public class PaymentResource extends JaxRsResourceBase {
                                                );
     }
 
-    //@Timed
+    @Timed
     @POST
     @Path("/{paymentId:" + UUID_PATTERN + "}/")
     @Consumes(APPLICATION_JSON)
@@ -213,7 +213,7 @@ public class PaymentResource extends JaxRsResourceBase {
         return uriBuilder.buildResponse(uriInfo, PaymentResource.class, "getPayment", payment.getId());
     }
 
-    //@Timed
+    @Timed
     @POST
     @Path("/{paymentId:" + UUID_PATTERN + "}/" + REFUNDS)
     @Consumes(APPLICATION_JSON)
@@ -239,7 +239,7 @@ public class PaymentResource extends JaxRsResourceBase {
         return uriBuilder.buildResponse(uriInfo, PaymentResource.class, "getPayment", payment.getId());
     }
 
-    //@Timed
+    @Timed
     @DELETE
     @Path("/{paymentId:" + UUID_PATTERN + "}/")
     @Consumes(APPLICATION_JSON)
@@ -264,7 +264,7 @@ public class PaymentResource extends JaxRsResourceBase {
         return uriBuilder.buildResponse(uriInfo, PaymentResource.class, "getPayment", payment.getId());
     }
 
-    //@Timed
+    @Timed
     @POST
     @Path("/{paymentId:" + UUID_PATTERN + "}/" + CHARGEBACKS)
     @Consumes(APPLICATION_JSON)
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/PluginResource.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/PluginResource.java
index afd9406..f581061 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/PluginResource.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/PluginResource.java
@@ -87,7 +87,7 @@ public class PluginResource extends JaxRsResourceBase {
         this.osgiServlet = osgiServlet;
     }
 
-    //@Timed
+    @Timed
     @DELETE
     public Response doDELETE(@javax.ws.rs.core.Context final HttpServletRequest request,
                              @javax.ws.rs.core.Context final HttpServletResponse response,
@@ -96,7 +96,7 @@ public class PluginResource extends JaxRsResourceBase {
         return serviceViaOSGIPlugin(request, response, servletContext, servletConfig);
     }
 
-    //@Timed
+    @Timed
     @GET
     public Response doGET(@javax.ws.rs.core.Context final HttpServletRequest request,
                           @javax.ws.rs.core.Context final HttpServletResponse response,
@@ -105,7 +105,7 @@ public class PluginResource extends JaxRsResourceBase {
         return serviceViaOSGIPlugin(request, response, servletContext, servletConfig);
     }
 
-    //@Timed
+    @Timed
     @OPTIONS
     public Response doOPTIONS(@javax.ws.rs.core.Context final HttpServletRequest request,
                               @javax.ws.rs.core.Context final HttpServletResponse response,
@@ -114,7 +114,7 @@ public class PluginResource extends JaxRsResourceBase {
         return serviceViaOSGIPlugin(request, response, servletContext, servletConfig);
     }
 
-    //@Timed
+    @Timed
     @POST
     @Consumes("application/x-www-form-urlencoded")
     public Response doFormPOST(final MultivaluedMap<String, String> form,
@@ -125,7 +125,7 @@ public class PluginResource extends JaxRsResourceBase {
         return serviceViaOSGIPlugin(form, request, response, servletContext, servletConfig);
     }
 
-    //@Timed
+    @Timed
     @POST
     public Response doPOST(@javax.ws.rs.core.Context final HttpServletRequest request,
                            @javax.ws.rs.core.Context final HttpServletResponse response,
@@ -134,7 +134,7 @@ public class PluginResource extends JaxRsResourceBase {
         return serviceViaOSGIPlugin(request, response, servletContext, servletConfig);
     }
 
-    //@Timed
+    @Timed
     @PUT
     public Response doPUT(@javax.ws.rs.core.Context final HttpServletRequest request,
                           @javax.ws.rs.core.Context final HttpServletResponse response,
@@ -143,7 +143,7 @@ public class PluginResource extends JaxRsResourceBase {
         return serviceViaOSGIPlugin(request, response, servletContext, servletConfig);
     }
 
-    //@Timed
+    @Timed
     @HEAD
     public Response doHEAD(@javax.ws.rs.core.Context final HttpServletRequest request,
                            @javax.ws.rs.core.Context final HttpServletResponse response,
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/SecurityResource.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/SecurityResource.java
index 730dd3c..c4348b5 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/SecurityResource.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/SecurityResource.java
@@ -70,7 +70,7 @@ public class SecurityResource extends JaxRsResourceBase {
         this.securityApi = securityApi;
     }
 
-    //@Timed
+    @Timed
     @GET
     @Path("/permissions")
     @Produces(APPLICATION_JSON)
@@ -80,7 +80,7 @@ public class SecurityResource extends JaxRsResourceBase {
         return Response.status(Status.OK).entity(json).build();
     }
 
-    //@Timed
+    @Timed
     @GET
     @Path("/subject")
     @Produces(APPLICATION_JSON)
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/SubscriptionResource.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/SubscriptionResource.java
index f7c22bd..ddd2768 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/SubscriptionResource.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/SubscriptionResource.java
@@ -111,7 +111,7 @@ public class SubscriptionResource extends JaxRsResourceBase {
         this.subscriptionApi = subscriptionApi;
     }
 
-    //@Timed
+    @Timed
     @GET
     @Path("/{subscriptionId:" + UUID_PATTERN + "}")
     @Produces(APPLICATION_JSON)
@@ -123,7 +123,7 @@ public class SubscriptionResource extends JaxRsResourceBase {
         return Response.status(Status.OK).entity(json).build();
     }
 
-    //@Timed
+    @Timed
     @POST
     @Consumes(APPLICATION_JSON)
     @Produces(APPLICATION_JSON)
@@ -168,7 +168,7 @@ public class SubscriptionResource extends JaxRsResourceBase {
         return callCompletionCreation.withSynchronization(callback, timeoutSec, callCompletion, callContext);
     }
 
-    //@Timed
+    @Timed
     @PUT
     @Path("/{subscriptionId:" + UUID_PATTERN + "}/uncancel")
     @Produces(APPLICATION_JSON)
@@ -183,7 +183,7 @@ public class SubscriptionResource extends JaxRsResourceBase {
         return Response.status(Status.OK).build();
     }
 
-    //@Timed
+    @Timed
     @PUT
     @Produces(APPLICATION_JSON)
     @Consumes(APPLICATION_JSON)
@@ -244,7 +244,7 @@ public class SubscriptionResource extends JaxRsResourceBase {
         return callCompletionCreation.withSynchronization(callback, timeoutSec, callCompletion, callContext);
     }
 
-    //@Timed
+    @Timed
     @DELETE
     @Path("/{subscriptionId:" + UUID_PATTERN + "}")
     @Produces(APPLICATION_JSON)
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/TagDefinitionResource.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/TagDefinitionResource.java
index 7238170..343dd43 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/TagDefinitionResource.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/TagDefinitionResource.java
@@ -74,7 +74,7 @@ public class TagDefinitionResource extends JaxRsResourceBase {
         super(uriBuilder, tagUserApi, customFieldUserApi, auditUserApi, accountUserApi, paymentApi, clock, context);
     }
 
-    //@Timed
+    @Timed
     @GET
     @Produces(APPLICATION_JSON)
     public Response getTagDefinitions(@javax.ws.rs.core.Context final HttpServletRequest request,
@@ -91,7 +91,7 @@ public class TagDefinitionResource extends JaxRsResourceBase {
         return Response.status(Status.OK).entity(result).build();
     }
 
-    //@Timed
+    @Timed
     @GET
     @Path("/{tagDefinitionId:" + UUID_PATTERN + "}")
     @Produces(APPLICATION_JSON)
@@ -105,7 +105,7 @@ public class TagDefinitionResource extends JaxRsResourceBase {
         return Response.status(Status.OK).entity(json).build();
     }
 
-    //@Timed
+    @Timed
     @POST
     @Consumes(APPLICATION_JSON)
     @Produces(APPLICATION_JSON)
@@ -123,7 +123,7 @@ public class TagDefinitionResource extends JaxRsResourceBase {
         return uriBuilder.buildResponse(uriInfo, TagDefinitionResource.class, "getTagDefinition", createdTagDef.getId());
     }
 
-    //@Timed
+    @Timed
     @DELETE
     @Path("/{tagDefinitionId:" + UUID_PATTERN + "}")
     @Produces(APPLICATION_JSON)
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/TagResource.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/TagResource.java
index 8c68487..4871ce2 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/TagResource.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/TagResource.java
@@ -72,7 +72,7 @@ public class TagResource extends JaxRsResourceBase {
         super(uriBuilder, tagUserApi, customFieldUserApi, auditUserApi, accountUserApi, paymentApi, clock, context);
     }
 
-    //@Timed
+    @Timed
     @GET
     @Path("/" + PAGINATION)
     @Produces(APPLICATION_JSON)
@@ -103,7 +103,7 @@ public class TagResource extends JaxRsResourceBase {
                                                 nextPageUri);
     }
 
-    //@Timed
+    @Timed
     @GET
     @Path("/" + SEARCH + "/{searchKey:" + ANYTHING_PATTERN + "}")
     @Produces(APPLICATION_JSON)
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/TenantResource.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/TenantResource.java
index 901801e..b0e6789 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/TenantResource.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/TenantResource.java
@@ -80,7 +80,7 @@ public class TenantResource extends JaxRsResourceBase {
         this.tenantApi = tenantApi;
     }
 
-    //@Timed
+    @Timed
     @GET
     @Path("/{tenantId:" + UUID_PATTERN + "}")
     @Produces(APPLICATION_JSON)
@@ -89,7 +89,7 @@ public class TenantResource extends JaxRsResourceBase {
         return Response.status(Status.OK).entity(new TenantJson(tenant)).build();
     }
 
-    //@Timed
+    @Timed
     @GET
     @Produces(APPLICATION_JSON)
     public Response getTenantByApiKey(@QueryParam(QUERY_API_KEY) final String externalKey) throws TenantApiException {
@@ -97,7 +97,7 @@ public class TenantResource extends JaxRsResourceBase {
         return Response.status(Status.OK).entity(new TenantJson(tenant)).build();
     }
 
-    //@Timed
+    @Timed
     @POST
     @Consumes(APPLICATION_JSON)
     @Produces(APPLICATION_JSON)
@@ -112,7 +112,7 @@ public class TenantResource extends JaxRsResourceBase {
         return uriBuilder.buildResponse(uriInfo, TenantResource.class, "getTenant", tenant.getId());
     }
 
-    //@Timed
+    @Timed
     @POST
     @Path("/" + REGISTER_NOTIFICATION_CALLBACK)
     @Consumes(APPLICATION_JSON)
@@ -129,7 +129,7 @@ public class TenantResource extends JaxRsResourceBase {
         return Response.created(uri).build();
     }
 
-    //@Timed
+    @Timed
     @GET
     @Path("/" + REGISTER_NOTIFICATION_CALLBACK)
     @Produces(APPLICATION_JSON)
@@ -141,7 +141,7 @@ public class TenantResource extends JaxRsResourceBase {
         return Response.status(Status.OK).entity(result).build();
     }
 
-    //@Timed
+    @Timed
     @DELETE
     @Path("/REGISTER_NOTIFICATION_CALLBACK")
     public Response deletePushNotificationCallbacks(@PathParam("tenantId") final String tenantId,
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/TransactionResource.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/TransactionResource.java
index 24fb0bb..d3bca8c 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/TransactionResource.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/TransactionResource.java
@@ -85,7 +85,7 @@ public class TransactionResource extends JaxRsResourceBase {
         super(uriBuilder, tagUserApi, customFieldUserApi, auditUserApi, accountUserApi, paymentApi, clock, context);
     }
 
-    //@Timed
+    @Timed
     @POST
     @Path("/{transactionId:" + UUID_PATTERN + "}/")
     @Consumes(APPLICATION_JSON)
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/UsageResource.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/UsageResource.java
index 9ebe55f..71ade06 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/UsageResource.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/UsageResource.java
@@ -16,8 +16,10 @@
 
 package org.killbill.billing.jaxrs.resources;
 
+import java.util.List;
 import java.util.UUID;
 
+import javax.annotation.concurrent.Immutable;
 import javax.inject.Inject;
 import javax.servlet.http.HttpServletRequest;
 import javax.ws.rs.Consumes;
@@ -32,13 +34,18 @@ import javax.ws.rs.core.Response;
 import javax.ws.rs.core.Response.Status;
 import javax.ws.rs.core.UriInfo;
 
-import org.joda.time.DateTime;
+import org.joda.time.LocalDate;
+import org.killbill.billing.account.api.AccountApiException;
 import org.killbill.billing.account.api.AccountUserApi;
-import org.killbill.billing.jaxrs.json.UsageJson;
+import org.killbill.billing.entitlement.api.EntitlementApi;
+import org.killbill.billing.entitlement.api.EntitlementApiException;
+import org.killbill.billing.jaxrs.json.RolledUpUsageJson;
+import org.killbill.billing.jaxrs.json.SubscriptionUsageRecordJson;
 import org.killbill.billing.jaxrs.util.Context;
 import org.killbill.billing.jaxrs.util.JaxrsUriBuilder;
 import org.killbill.billing.payment.api.PaymentApi;
 import org.killbill.billing.usage.api.RolledUpUsage;
+import org.killbill.billing.usage.api.SubscriptionUsageRecord;
 import org.killbill.billing.usage.api.UsageUserApi;
 import org.killbill.billing.util.api.AuditUserApi;
 import org.killbill.billing.util.api.CustomFieldUserApi;
@@ -48,6 +55,8 @@ import org.killbill.billing.util.callcontext.TenantContext;
 import org.killbill.clock.Clock;
 
 import com.codahale.metrics.annotation.Timed;
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableList;
 import com.google.inject.Singleton;
 import static javax.ws.rs.core.MediaType.APPLICATION_JSON;
 
@@ -56,6 +65,7 @@ import static javax.ws.rs.core.MediaType.APPLICATION_JSON;
 public class UsageResource extends JaxRsResourceBase {
 
     private final UsageUserApi usageUserApi;
+    private final EntitlementApi entitlementApi;
 
     @Inject
     public UsageResource(final JaxrsUriBuilder uriBuilder,
@@ -65,45 +75,76 @@ public class UsageResource extends JaxRsResourceBase {
                          final AccountUserApi accountUserApi,
                          final UsageUserApi usageUserApi,
                          final PaymentApi paymentApi,
+                         final EntitlementApi entitlementApi,
                          final Clock clock,
                          final Context context) {
         super(uriBuilder, tagUserApi, customFieldUserApi, auditUserApi, accountUserApi, paymentApi, clock, context);
         this.usageUserApi = usageUserApi;
+        this.entitlementApi = entitlementApi;
     }
 
-    //@Timed
+    @Timed
     @POST
     @Consumes(APPLICATION_JSON)
     @Produces(APPLICATION_JSON)
-    public Response recordUsage(final UsageJson json,
+    public Response recordUsage(final SubscriptionUsageRecordJson json,
                                   @HeaderParam(HDR_CREATED_BY) final String createdBy,
                                   @HeaderParam(HDR_REASON) final String reason,
                                   @HeaderParam(HDR_COMMENT) final String comment,
                                   @javax.ws.rs.core.Context final HttpServletRequest request,
-                                  @javax.ws.rs.core.Context final UriInfo uriInfo)  {
+                                  @javax.ws.rs.core.Context final UriInfo uriInfo) throws EntitlementApiException, AccountApiException {
+
+        Preconditions.checkNotNull(json.getSubscriptionId());
+        Preconditions.checkNotNull(json.getUnitUsageRecords());
+        Preconditions.checkArgument(!json.getUnitUsageRecords().isEmpty());
 
         final CallContext callContext = context.createContext(createdBy, reason, comment, request);
-        usageUserApi.recordRolledUpUsage(UUID.fromString(json.getSubscriptionId()), json.getUnitType(), json.getStartTime(), json.getEndTime(), json.getAmount(), callContext);
+        // Verify subscription exists..
+        entitlementApi.getEntitlementForId(UUID.fromString(json.getSubscriptionId()), callContext);
+
+        final SubscriptionUsageRecord record = json.toSubscriptionUsageRecord();
+        usageUserApi.recordRolledUpUsage(record, callContext);
         return Response.status(Status.CREATED).build();
     }
 
-    //@Timed
+    @Timed
     @GET
     @Path("/{subscriptionId:" + UUID_PATTERN + "}/{unitType}")
     @Produces(APPLICATION_JSON)
     public Response getUsage(@PathParam("subscriptionId") final String subscriptionId,
-                               @PathParam("unitType") final String unitType,
-                               @QueryParam(QUERY_START_TIME) final String startTime,
-                               @QueryParam(QUERY_END_TIME) final String endTime,
-                               @javax.ws.rs.core.Context final HttpServletRequest request)  {
+                             @PathParam("unitType") final String unitType,
+                             @QueryParam(QUERY_START_DATE) final String startDate,
+                             @QueryParam(QUERY_END_DATE) final String endDate,
+                             @javax.ws.rs.core.Context final HttpServletRequest request)  {
+
+        final TenantContext tenantContext = context.createContext(request);
+
+        final LocalDate usageStartDate = LOCAL_DATE_FORMATTER.parseLocalDate(startDate);
+        final LocalDate usageEndDate = LOCAL_DATE_FORMATTER.parseLocalDate(endDate);
+
+        final RolledUpUsage usage = usageUserApi.getUsageForSubscription(UUID.fromString(subscriptionId), unitType, usageStartDate, usageEndDate, tenantContext);
+        final RolledUpUsageJson result = new RolledUpUsageJson(usage);
+        return Response.status(Status.OK).entity(result).build();
+    }
+
+    @Timed
+    @GET
+    @Path("/{subscriptionId:" + UUID_PATTERN + "}")
+    @Produces(APPLICATION_JSON)
+    public Response getAllUsage(@PathParam("subscriptionId") final String subscriptionId,
+                             @QueryParam(QUERY_START_DATE) final String startDate,
+                             @QueryParam(QUERY_END_DATE) final String endDate,
+                             @javax.ws.rs.core.Context final HttpServletRequest request)  {
 
         final TenantContext tenantContext = context.createContext(request);
 
-        final DateTime usageStartTime = DATE_TIME_FORMATTER.parseDateTime(startTime);
-        final DateTime usageEndTime = DATE_TIME_FORMATTER.parseDateTime(endTime);
+        final LocalDate usageStartDate = LOCAL_DATE_FORMATTER.parseLocalDate(startDate);
+        final LocalDate usageEndDate = LOCAL_DATE_FORMATTER.parseLocalDate(endDate);
 
-        final RolledUpUsage usage = usageUserApi.getUsageForSubscription(UUID.fromString(subscriptionId), unitType, usageStartTime, usageEndTime, tenantContext);
-        final UsageJson result = new UsageJson(usage);
+        // The current JAXRS API only allows to look for one transition
+        final List<LocalDate> startEndDate = ImmutableList.<LocalDate>builder().add(usageStartDate).add(usageEndDate).build();
+        final List<RolledUpUsage> usage = usageUserApi.getAllUsageForSubscription(UUID.fromString(subscriptionId), startEndDate, tenantContext);
+        final RolledUpUsageJson result = new RolledUpUsageJson(usage.get(0));
         return Response.status(Status.OK).entity(result).build();
     }
 

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

diff --git a/junction/pom.xml b/junction/pom.xml
index 0fd4150..fe10ae8 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.11.10-SNAPSHOT</version>
+        <version>0.11.11-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>killbill-junction</artifactId>

NEWS 3(+3 -0)

diff --git a/NEWS b/NEWS
index 6dd213b..25fa17e 100644
--- a/NEWS
+++ b/NEWS
@@ -1,4 +1,7 @@
 0.11.10
+    Payment bug fixes
+    New Janitor tasks
+    JMX properties refinment
     Fix broken logging (war artifacts)
     https://github.com/killbill/killbill/issues/210
     Update killbill-oss-parent to 0.7.22

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

diff --git a/overdue/pom.xml b/overdue/pom.xml
index 788e1f8..abc1d35 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.11.10-SNAPSHOT</version>
+        <version>0.11.11-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>killbill-overdue</artifactId>

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

diff --git a/payment/pom.xml b/payment/pom.xml
index 8cc8597..5e5b40f 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.11.10-SNAPSHOT</version>
+        <version>0.11.11-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>killbill-payment</artifactId>
diff --git a/payment/src/main/java/org/killbill/billing/payment/core/janitor/AttemptCompletionTask.java b/payment/src/main/java/org/killbill/billing/payment/core/janitor/AttemptCompletionTask.java
new file mode 100644
index 0000000..5714907
--- /dev/null
+++ b/payment/src/main/java/org/killbill/billing/payment/core/janitor/AttemptCompletionTask.java
@@ -0,0 +1,128 @@
+/*
+ * Copyright 2014 Groupon, Inc
+ * Copyright 2014 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.payment.core.janitor;
+
+import java.util.List;
+
+import org.killbill.billing.ObjectType;
+import org.killbill.billing.account.api.Account;
+import org.killbill.billing.account.api.AccountApiException;
+import org.killbill.billing.account.api.AccountInternalApi;
+import org.killbill.billing.callcontext.InternalCallContext;
+import org.killbill.billing.callcontext.InternalTenantContext;
+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.core.sm.PaymentStateMachineHelper;
+import org.killbill.billing.payment.core.sm.PluginControlledPaymentAutomatonRunner;
+import org.killbill.billing.payment.core.sm.RetryStateMachineHelper;
+import org.killbill.billing.payment.core.sm.RetryablePaymentStateContext;
+import org.killbill.billing.payment.dao.PaymentAttemptModelDao;
+import org.killbill.billing.payment.dao.PaymentDao;
+import org.killbill.billing.payment.dao.PaymentTransactionModelDao;
+import org.killbill.billing.payment.dao.PluginPropertySerializer;
+import org.killbill.billing.payment.dao.PluginPropertySerializer.PluginPropertySerializerException;
+import org.killbill.billing.payment.plugin.api.PaymentPluginApi;
+import org.killbill.billing.util.cache.CacheControllerDispatcher;
+import org.killbill.billing.util.callcontext.CallContext;
+import org.killbill.billing.util.callcontext.InternalCallContextFactory;
+import org.killbill.billing.util.config.PaymentConfig;
+import org.killbill.billing.util.dao.NonEntityDao;
+import org.killbill.clock.Clock;
+
+import com.google.common.base.Predicate;
+import com.google.common.collect.Iterables;
+
+/**
+ * Task to complete 'partially' incomplete payment attempts. Tis only matters for calls that went through PaymentControl apis.
+ * <p/>
+ * If the state of the transaction associated with the attempt completed, but the attempt state machine did not,
+ * we rerun the retry state machine to complete the call and transition the attempt into a terminal state.
+ */
+final class AttemptCompletionTask extends CompletionTaskBase<PaymentAttemptModelDao> {
+
+    public AttemptCompletionTask(final Janitor janitor, final InternalCallContextFactory internalCallContextFactory, final PaymentConfig paymentConfig,
+                                 final NonEntityDao nonEntityDao, final PaymentDao paymentDao, final Clock clock, final PaymentStateMachineHelper paymentStateMachineHelper,
+                                 final RetryStateMachineHelper retrySMHelper, final CacheControllerDispatcher controllerDispatcher, final AccountInternalApi accountInternalApi,
+                                 final PluginControlledPaymentAutomatonRunner pluginControlledPaymentAutomatonRunner, final OSGIServiceRegistration<PaymentPluginApi> pluginRegistry) {
+        super(janitor, internalCallContextFactory, paymentConfig, nonEntityDao, paymentDao, clock, paymentStateMachineHelper, retrySMHelper, controllerDispatcher, accountInternalApi, pluginControlledPaymentAutomatonRunner, pluginRegistry);
+    }
+
+    @Override
+    public List<PaymentAttemptModelDao> getItemsForIteration() {
+        final List<PaymentAttemptModelDao> incompleteAttempts = paymentDao.getPaymentAttemptsByState(retrySMHelper.getInitialState().getName(), getCreatedDateBefore(), completionTaskCallContext);
+        log.info("Janitor AttemptCompletionTask start run : found " + incompleteAttempts.size() + " incomplete attempts");
+        return incompleteAttempts;
+    }
+
+    @Override
+    public void doIteration(final PaymentAttemptModelDao attempt) {
+        final InternalTenantContext tenantContext = internalCallContextFactory.createInternalTenantContext(attempt.getAccountId(), attempt.getId(), ObjectType.PAYMENT_ATTEMPT);
+        final CallContext callContext = createCallContext("AttemptCompletionJanitorTask", tenantContext);
+        final InternalCallContext internalCallContext = internalCallContextFactory.createInternalCallContext(attempt.getAccountId(), callContext);
+
+        final List<PaymentTransactionModelDao> transactions = paymentDao.getPaymentTransactionsByExternalKey(attempt.getTransactionExternalKey(), tenantContext);
+        final PaymentTransactionModelDao transaction = Iterables.tryFind(transactions, new Predicate<PaymentTransactionModelDao>() {
+            @Override
+            public boolean apply(final PaymentTransactionModelDao input) {
+                return input.getAttemptId().equals(attempt.getId()) &&
+                       input.getTransactionStatus() == TransactionStatus.SUCCESS;
+            }
+        }).orNull();
+
+        if (transaction == null) {
+            log.info("Janitor AttemptCompletionTask moving attempt " + attempt.getId() + " -> ABORTED");
+            paymentDao.updatePaymentAttempt(attempt.getId(), attempt.getTransactionId(), "ABORTED", internalCallContext);
+            return;
+        }
+
+        try {
+            log.info("Janitor AttemptCompletionTask completing attempt " + attempt.getId() + " -> SUCCESS");
+
+            final Account account = accountInternalApi.getAccountById(attempt.getAccountId(), tenantContext);
+            final boolean isApiPayment = true; // unclear
+            final RetryablePaymentStateContext paymentStateContext = new RetryablePaymentStateContext(attempt.getPluginName(),
+                                                                                                      isApiPayment,
+                                                                                                      transaction.getPaymentId(),
+                                                                                                      attempt.getPaymentExternalKey(),
+                                                                                                      transaction.getTransactionExternalKey(),
+                                                                                                      transaction.getTransactionType(),
+                                                                                                      account,
+                                                                                                      attempt.getPaymentMethodId(),
+                                                                                                      transaction.getAmount(),
+                                                                                                      transaction.getCurrency(),
+                                                                                                      PluginPropertySerializer.deserialize(attempt.getPluginProperties()),
+                                                                                                      internalCallContext,
+                                                                                                      callContext);
+
+            paymentStateContext.setAttemptId(attempt.getId()); // Normally set by leavingState Callback
+            paymentStateContext.setPaymentTransactionModelDao(transaction); // Normally set by raw state machine
+            //
+            // Will rerun the state machine with special callbacks to only make the onCompletion call
+            // to the PaymentControlPluginApi plugin and transition the state.
+            //
+            pluginControlledPaymentAutomatonRunner.completeRun(paymentStateContext);
+        } catch (AccountApiException e) {
+            log.warn("Janitor AttemptCompletionTask failed to complete payment attempt " + attempt.getId(), e);
+        } catch (PluginPropertySerializerException e) {
+            log.warn("Janitor AttemptCompletionTask failed to complete payment attempt " + attempt.getId(), e);
+        } catch (PaymentApiException e) {
+            log.warn("Janitor AttemptCompletionTask failed to complete payment attempt " + attempt.getId(), e);
+        }
+    }
+}
diff --git a/payment/src/main/java/org/killbill/billing/payment/core/janitor/CompletionTaskBase.java b/payment/src/main/java/org/killbill/billing/payment/core/janitor/CompletionTaskBase.java
new file mode 100644
index 0000000..9f68a5e
--- /dev/null
+++ b/payment/src/main/java/org/killbill/billing/payment/core/janitor/CompletionTaskBase.java
@@ -0,0 +1,124 @@
+/*
+ * Copyright 2014 Groupon, Inc
+ * Copyright 2014 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.payment.core.janitor;
+
+import java.util.List;
+import java.util.UUID;
+
+import org.joda.time.DateTime;
+import org.killbill.billing.ObjectType;
+import org.killbill.billing.account.api.AccountInternalApi;
+import org.killbill.billing.callcontext.DefaultCallContext;
+import org.killbill.billing.callcontext.InternalCallContext;
+import org.killbill.billing.callcontext.InternalTenantContext;
+import org.killbill.billing.osgi.api.OSGIServiceRegistration;
+import org.killbill.billing.payment.core.sm.PaymentStateMachineHelper;
+import org.killbill.billing.payment.core.sm.PluginControlledPaymentAutomatonRunner;
+import org.killbill.billing.payment.core.sm.RetryStateMachineHelper;
+import org.killbill.billing.payment.dao.PaymentDao;
+import org.killbill.billing.payment.plugin.api.PaymentPluginApi;
+import org.killbill.billing.util.cache.Cachable.CacheType;
+import org.killbill.billing.util.cache.CacheControllerDispatcher;
+import org.killbill.billing.util.callcontext.CallContext;
+import org.killbill.billing.util.callcontext.CallOrigin;
+import org.killbill.billing.util.callcontext.InternalCallContextFactory;
+import org.killbill.billing.util.callcontext.UserType;
+import org.killbill.billing.util.config.PaymentConfig;
+import org.killbill.billing.util.dao.NonEntityDao;
+import org.killbill.clock.Clock;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+abstract class CompletionTaskBase<T> implements Runnable {
+
+    protected Logger log = LoggerFactory.getLogger(CompletionTaskBase.class);
+
+    private Janitor janitor;
+    private final String taskName;
+
+    protected final PaymentConfig paymentConfig;
+    protected final Clock clock;
+    protected final PaymentDao paymentDao;
+    protected final InternalCallContext completionTaskCallContext;
+    protected final InternalCallContextFactory internalCallContextFactory;
+    protected final NonEntityDao nonEntityDao;
+    protected final PaymentStateMachineHelper paymentStateMachineHelper;
+    protected final RetryStateMachineHelper retrySMHelper;
+    protected final CacheControllerDispatcher controllerDispatcher;
+    protected final AccountInternalApi accountInternalApi;
+    protected final PluginControlledPaymentAutomatonRunner pluginControlledPaymentAutomatonRunner;
+    protected final OSGIServiceRegistration<PaymentPluginApi> pluginRegistry;
+
+
+    public CompletionTaskBase(final Janitor janitor, final InternalCallContextFactory internalCallContextFactory, final PaymentConfig paymentConfig,
+                              final NonEntityDao nonEntityDao, final PaymentDao paymentDao, final Clock clock, final PaymentStateMachineHelper paymentStateMachineHelper,
+                              final RetryStateMachineHelper retrySMHelper, final CacheControllerDispatcher controllerDispatcher, final AccountInternalApi accountInternalApi,
+                              final PluginControlledPaymentAutomatonRunner pluginControlledPaymentAutomatonRunner, final OSGIServiceRegistration<PaymentPluginApi> pluginRegistry) {
+        this.janitor = janitor;
+        this.internalCallContextFactory = internalCallContextFactory;
+        this.paymentConfig = paymentConfig;
+        this.nonEntityDao = nonEntityDao;
+        this.paymentDao = paymentDao;
+        this.clock = clock;
+        this.paymentStateMachineHelper = paymentStateMachineHelper;
+        this.retrySMHelper = retrySMHelper;
+        this.controllerDispatcher = controllerDispatcher;
+        this.accountInternalApi = accountInternalApi;
+        this.pluginControlledPaymentAutomatonRunner = pluginControlledPaymentAutomatonRunner;
+        this.pluginRegistry = pluginRegistry;
+        this.taskName = this.getClass().getName();
+        this.completionTaskCallContext = internalCallContextFactory.createInternalCallContext((Long) null, (Long) null, taskName, CallOrigin.INTERNAL, UserType.SYSTEM, UUID.randomUUID());
+    }
+
+    @Override
+    public void run() {
+
+        if (janitor.isStopped()) {
+            log.info("Janitor Task " + taskName + " was requested to stop");
+            return;
+        }
+        final List<T> items = getItemsForIteration();
+        for (T item : items) {
+            if (janitor.isStopped()) {
+                log.info("Janitor Task " + taskName + " was requested to stop");
+                return;
+            }
+            try {
+                doIteration(item);
+            } catch(IllegalStateException e) {
+                log.warn(e.getMessage());
+            }
+        }
+    }
+
+    public abstract List<T> getItemsForIteration();
+
+    public abstract void doIteration(final T item);
+
+    protected CallContext createCallContext(final String taskName, final InternalTenantContext tenantContext) {
+        final UUID tenantId = nonEntityDao.retrieveIdFromObject(tenantContext.getTenantRecordId(), ObjectType.TENANT, controllerDispatcher.getCacheController(CacheType.OBJECT_ID));
+        final CallContext callContext = new DefaultCallContext(tenantId, taskName, CallOrigin.INTERNAL, UserType.SYSTEM, UUID.randomUUID(), clock);
+        return callContext;
+    }
+
+
+    protected DateTime getCreatedDateBefore() {
+        final long delayBeforeNowMs = paymentConfig.getJanitorPendingCleanupTime().getMillis();
+        return clock.getUTCNow().minusMillis((int) delayBeforeNowMs);
+    }
+}
diff --git a/payment/src/main/java/org/killbill/billing/payment/core/janitor/ErroredPaymentTask.java b/payment/src/main/java/org/killbill/billing/payment/core/janitor/ErroredPaymentTask.java
new file mode 100644
index 0000000..fd6d9f3
--- /dev/null
+++ b/payment/src/main/java/org/killbill/billing/payment/core/janitor/ErroredPaymentTask.java
@@ -0,0 +1,163 @@
+/*
+ * Copyright 2014 Groupon, Inc
+ * Copyright 2014 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.payment.core.janitor;
+
+import java.math.BigDecimal;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.annotation.Nullable;
+
+import org.joda.time.DateTime;
+import org.killbill.automaton.StateMachine;
+import org.killbill.billing.ObjectType;
+import org.killbill.billing.account.api.AccountInternalApi;
+import org.killbill.billing.callcontext.InternalCallContext;
+import org.killbill.billing.callcontext.InternalTenantContext;
+import org.killbill.billing.catalog.api.Currency;
+import org.killbill.billing.osgi.api.OSGIServiceRegistration;
+import org.killbill.billing.payment.api.PluginProperty;
+import org.killbill.billing.payment.api.TransactionStatus;
+import org.killbill.billing.payment.core.sm.PaymentEnteringStateCallback;
+import org.killbill.billing.payment.core.sm.PaymentStateMachineHelper;
+import org.killbill.billing.payment.core.sm.PluginControlledPaymentAutomatonRunner;
+import org.killbill.billing.payment.core.sm.RetryStateMachineHelper;
+import org.killbill.billing.payment.dao.PaymentDao;
+import org.killbill.billing.payment.dao.PaymentMethodModelDao;
+import org.killbill.billing.payment.dao.PaymentModelDao;
+import org.killbill.billing.payment.dao.PaymentTransactionModelDao;
+import org.killbill.billing.payment.plugin.api.PaymentPluginApi;
+import org.killbill.billing.payment.plugin.api.PaymentPluginApiException;
+import org.killbill.billing.payment.plugin.api.PaymentTransactionInfoPlugin;
+import org.killbill.billing.util.cache.CacheControllerDispatcher;
+import org.killbill.billing.util.callcontext.CallContext;
+import org.killbill.billing.util.callcontext.InternalCallContextFactory;
+import org.killbill.billing.util.config.PaymentConfig;
+import org.killbill.billing.util.dao.NonEntityDao;
+import org.killbill.clock.Clock;
+
+import com.google.common.base.Preconditions;
+import com.google.common.base.Predicate;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
+
+public class ErroredPaymentTask extends CompletionTaskBase<PaymentModelDao> {
+
+    // We could configure all that if this becomes useful but we also want to avoid a flurry of parameters.
+    private static final int SAFETY_DELAY_MS = (3 * 60 * 1000); // 3 minutes
+    private final int OLDER_PAYMENTS_IN_DAYS = 3; // don't look at ERRORED payment older than 3 days
+    private final int MAX_ITEMS_PER_LOOP = 100; // Limit of items per iteration
+
+    public ErroredPaymentTask(final Janitor janitor, final InternalCallContextFactory internalCallContextFactory, final PaymentConfig paymentConfig,
+                                 final NonEntityDao nonEntityDao, final PaymentDao paymentDao, final Clock clock,
+                                 final PaymentStateMachineHelper paymentStateMachineHelper, final RetryStateMachineHelper retrySMHelper, final CacheControllerDispatcher controllerDispatcher, final AccountInternalApi accountInternalApi,
+                                 final PluginControlledPaymentAutomatonRunner pluginControlledPaymentAutomatonRunner, final OSGIServiceRegistration<PaymentPluginApi> pluginRegistry) {
+        super(janitor, internalCallContextFactory, paymentConfig, nonEntityDao, paymentDao, clock, paymentStateMachineHelper, retrySMHelper, controllerDispatcher, accountInternalApi, pluginControlledPaymentAutomatonRunner, pluginRegistry);
+    }
+
+    @Override
+    public List<PaymentModelDao> getItemsForIteration() {
+        // In theory this should be the plugin timeout but we add a 3 minutes delay for safety.
+        int delayBeforeNow = (int) paymentConfig.getPaymentPluginTimeout().getMillis() + SAFETY_DELAY_MS;
+        final DateTime createdBeforeDate = clock.getUTCNow().minusMillis(delayBeforeNow);
+
+        // We want to avoid iterating on the same failed payments -- if for some reasons they can't fix themselves.
+        final DateTime createdAfterDate = clock.getUTCNow().minusDays(OLDER_PAYMENTS_IN_DAYS);
+
+        final List<PaymentModelDao> result = paymentDao.getPaymentsByStates(paymentStateMachineHelper.getErroredStateNames(), createdBeforeDate, createdAfterDate, MAX_ITEMS_PER_LOOP, completionTaskCallContext);
+        return result;
+    }
+
+    @Override
+    public void doIteration(final PaymentModelDao item) {
+
+        final InternalTenantContext internalTenantContext = internalCallContextFactory.createInternalTenantContext(item.getAccountId(), item.getId(), ObjectType.PAYMENT);
+        final CallContext callContext = createCallContext("ErroredPaymentTask", internalTenantContext);
+        final InternalCallContext internalCallContext = internalCallContextFactory.createInternalCallContext(item.getAccountId(), callContext);
+
+        final List<PaymentTransactionModelDao> transactions = paymentDao.getTransactionsForPayment(item.getId(), internalTenantContext);
+        Preconditions.checkState(! transactions.isEmpty(), "Janitor ErroredPaymentTask found item " + item.getId() + " with no transactions, skipping");
+
+        // We look for latest transaction in an UNKNOWN state, if not we skip
+        final PaymentTransactionModelDao unknownTransaction = transactions.get(transactions.size() - 1);
+        if (unknownTransaction.getTransactionStatus() != TransactionStatus.UNKNOWN) {
+            return;
+        }
+
+        final PaymentMethodModelDao paymentMethod = paymentDao.getPaymentMethod(item.getPaymentMethodId(), internalCallContext);
+        final PaymentPluginApi paymentPluginApi = getPaymentPluginApi(item, paymentMethod.getPluginName());
+
+
+        PaymentTransactionInfoPlugin pluginErroredTransaction = null;
+        try {
+            final List<PaymentTransactionInfoPlugin> result = paymentPluginApi.getPaymentInfo(item.getAccountId(), item.getId(), ImmutableList.<PluginProperty>of(), callContext);
+
+            pluginErroredTransaction = Iterables.tryFind(result, new Predicate<PaymentTransactionInfoPlugin>() {
+                @Override
+                public boolean apply(@Nullable final PaymentTransactionInfoPlugin input) {
+                    return input.getKbTransactionPaymentId().equals(unknownTransaction.getId());
+                }
+            }).orNull();
+        } catch (PaymentPluginApiException ignored) {
+
+        }
+
+        // Compute new transactionStatus based on pluginInfo state; and if that did not change, bail early.
+        final TransactionStatus transactionStatus = PaymentEnteringStateCallback.paymentPluginStatusToTransactionStatus(pluginErroredTransaction);
+        if (transactionStatus == unknownTransaction.getTransactionStatus()) {
+            return;
+        }
+
+        // This piece of logic is obviously outside of the state machine, and this is a bit of a hack; at least all the paymentStates internal config is
+        // kept into paymentStateMachineHelper.
+        final String newPaymentState;
+        switch (transactionStatus) {
+            case PENDING:
+                newPaymentState = paymentStateMachineHelper.getPendingStateForTransaction(unknownTransaction.getTransactionType());
+                break;
+            case SUCCESS:
+                newPaymentState = paymentStateMachineHelper.getSuccessfulStateForTransaction(unknownTransaction.getTransactionType());
+                break;
+            case PAYMENT_FAILURE:
+                newPaymentState = paymentStateMachineHelper.getFailureStateForTransaction(unknownTransaction.getTransactionType());
+                break;
+            case PLUGIN_FAILURE:
+            case UNKNOWN:
+            default:
+                newPaymentState = paymentStateMachineHelper.getErroredStateForTransaction(unknownTransaction.getTransactionType());
+                break;
+        }
+        final String lastSuccessPaymentState = paymentStateMachineHelper.isSuccessState(newPaymentState) ? newPaymentState : null;
+
+
+        final BigDecimal processedAmount = pluginErroredTransaction != null ?  pluginErroredTransaction.getAmount() : null;
+        final Currency processedCurrency = pluginErroredTransaction != null ? pluginErroredTransaction.getCurrency() : null;
+        final String gatewayErrorCode = pluginErroredTransaction != null ? pluginErroredTransaction.getGatewayErrorCode() : null;
+        final String gatewayError = pluginErroredTransaction != null ? pluginErroredTransaction.getGatewayError() : null;
+
+        paymentDao.updatePaymentAndTransactionOnCompletion(item.getAccountId(), item.getId(), unknownTransaction.getTransactionType(), newPaymentState, lastSuccessPaymentState,
+                                                           unknownTransaction.getId(), transactionStatus, processedAmount, processedCurrency, gatewayErrorCode, gatewayError, internalCallContext);
+
+    }
+
+    private PaymentPluginApi getPaymentPluginApi(final PaymentModelDao item, final String pluginName) {
+        final PaymentPluginApi pluginApi = pluginRegistry.getServiceForName(pluginName);
+        Preconditions.checkState(pluginApi != null, "Janitor ErroredPaymentTask cannot retrieve PaymentPluginApi " + item.getId() + ", skipping");
+        return pluginApi;
+    }
+}
diff --git a/payment/src/main/java/org/killbill/billing/payment/core/janitor/Janitor.java b/payment/src/main/java/org/killbill/billing/payment/core/janitor/Janitor.java
new file mode 100644
index 0000000..0bc4c0e
--- /dev/null
+++ b/payment/src/main/java/org/killbill/billing/payment/core/janitor/Janitor.java
@@ -0,0 +1,132 @@
+/*
+ * Copyright 2014 Groupon, Inc
+ * Copyright 2014 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.payment.core.janitor;
+
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
+
+import javax.inject.Inject;
+import javax.inject.Named;
+
+import org.killbill.billing.account.api.AccountInternalApi;
+import org.killbill.billing.osgi.api.OSGIServiceRegistration;
+import org.killbill.billing.payment.core.sm.PaymentStateMachineHelper;
+import org.killbill.billing.payment.core.sm.PluginControlledPaymentAutomatonRunner;
+import org.killbill.billing.payment.core.sm.RetryStateMachineHelper;
+import org.killbill.billing.payment.dao.PaymentDao;
+import org.killbill.billing.payment.glue.PaymentModule;
+import org.killbill.billing.payment.plugin.api.PaymentPluginApi;
+import org.killbill.billing.util.cache.CacheControllerDispatcher;
+import org.killbill.billing.util.callcontext.InternalCallContextFactory;
+import org.killbill.billing.util.config.PaymentConfig;
+import org.killbill.billing.util.dao.NonEntityDao;
+import org.killbill.clock.Clock;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Takes care of incomplete payment/transactions.
+ */
+public class Janitor {
+
+    private final static Logger log = LoggerFactory.getLogger(Janitor.class);
+
+    private final static int TERMINATION_TIMEOUT_SEC = 5;
+
+    private final ScheduledExecutorService janitorExecutor;
+    private final PaymentConfig paymentConfig;
+    private final PendingTransactionTask pendingTransactionTask;
+    private final AttemptCompletionTask attemptCompletionTask;
+    private final ErroredPaymentTask erroredPaymentCompletionTask;
+
+    private volatile boolean isStopped;
+
+    @Inject
+    public Janitor(final AccountInternalApi accountInternalApi,
+                   final PaymentDao paymentDao,
+                   final PaymentConfig paymentConfig,
+                   final Clock clock,
+                   final NonEntityDao nonEntityDao,
+                   final InternalCallContextFactory internalCallContextFactory,
+                   final PluginControlledPaymentAutomatonRunner pluginControlledPaymentAutomatonRunner,
+                   @Named(PaymentModule.JANITOR_EXECUTOR_NAMED) final ScheduledExecutorService janitorExecutor,
+                   final PaymentStateMachineHelper paymentSMHelper,
+                   final RetryStateMachineHelper retrySMHelper,
+                   final CacheControllerDispatcher controllerDispatcher,
+                   final OSGIServiceRegistration<PaymentPluginApi> pluginRegistry) {
+        this.janitorExecutor = janitorExecutor;
+        this.paymentConfig = paymentConfig;
+        this.pendingTransactionTask = new PendingTransactionTask(this, internalCallContextFactory, paymentConfig, nonEntityDao, paymentDao, clock, paymentSMHelper, retrySMHelper,
+                                                                 controllerDispatcher, accountInternalApi, pluginControlledPaymentAutomatonRunner, pluginRegistry);
+        this.attemptCompletionTask = new AttemptCompletionTask(this, internalCallContextFactory, paymentConfig, nonEntityDao, paymentDao, clock, paymentSMHelper, retrySMHelper,
+                                                               controllerDispatcher, accountInternalApi, pluginControlledPaymentAutomatonRunner, pluginRegistry);
+        this.erroredPaymentCompletionTask = new ErroredPaymentTask(this, internalCallContextFactory, paymentConfig, nonEntityDao, paymentDao, clock, paymentSMHelper, retrySMHelper,
+                                                               controllerDispatcher, accountInternalApi, pluginControlledPaymentAutomatonRunner, pluginRegistry);
+        this.isStopped = false;
+    }
+
+    public void start() {
+        if (isStopped) {
+            log.warn("Janitor is not a restartable service, and was already started, aborting");
+            return;
+        }
+
+        // Start task for removing old pending payments.
+        final TimeUnit pendingRateUnit = paymentConfig.getJanitorRunningRate().getUnit();
+        final long pendingPeriod = paymentConfig.getJanitorRunningRate().getPeriod();
+        janitorExecutor.scheduleAtFixedRate(pendingTransactionTask, pendingPeriod, pendingPeriod, pendingRateUnit);
+
+        // Start task for completing incomplete payment attempts
+        final TimeUnit attemptCompletionRateUnit = paymentConfig.getJanitorRunningRate().getUnit();
+        final long attemptCompletionPeriod = paymentConfig.getJanitorRunningRate().getPeriod();
+        janitorExecutor.scheduleAtFixedRate(attemptCompletionTask, attemptCompletionPeriod, attemptCompletionPeriod, attemptCompletionRateUnit);
+
+        // Start task for completing incomplete payment attempts
+        final TimeUnit erroredCompletionRateUnit = paymentConfig.getJanitorRunningRate().getUnit();
+        final long erroredCompletionPeriod = paymentConfig.getJanitorRunningRate().getPeriod();
+        janitorExecutor.scheduleAtFixedRate(erroredPaymentCompletionTask, erroredCompletionPeriod, erroredCompletionPeriod, erroredCompletionRateUnit);
+    }
+
+    public void stop() {
+        if (isStopped) {
+            log.warn("Janitor is already in a stopped state");
+            return;
+        }
+        try {
+            /* Previously submitted tasks will be executed with shutdown(); when task executes as a result of shutdown being called
+             * or because it was already in its execution loop, it will check for the volatile boolean isStopped flag and
+             * return immediately.
+             * Then, awaitTermination with a timeout is required to ensure tasks completed.
+             */
+            janitorExecutor.shutdown();
+            boolean success = janitorExecutor.awaitTermination(TERMINATION_TIMEOUT_SEC, TimeUnit.SECONDS);
+            if (!success) {
+                log.warn("Janitor failed to complete termination within " + TERMINATION_TIMEOUT_SEC + "sec");
+            }
+        } catch (InterruptedException e) {
+            Thread.currentThread().interrupt();
+            log.warn("Janitor stop sequence got interrupted");
+        } finally {
+            isStopped = true;
+        }
+    }
+
+    public boolean isStopped() {
+        return isStopped;
+    }
+}
diff --git a/payment/src/main/java/org/killbill/billing/payment/core/janitor/PendingTransactionTask.java b/payment/src/main/java/org/killbill/billing/payment/core/janitor/PendingTransactionTask.java
new file mode 100644
index 0000000..99369a2
--- /dev/null
+++ b/payment/src/main/java/org/killbill/billing/payment/core/janitor/PendingTransactionTask.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2014 Groupon, Inc
+ * Copyright 2014 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.payment.core.janitor;
+
+import java.util.List;
+
+import org.killbill.billing.account.api.AccountInternalApi;
+import org.killbill.billing.osgi.api.OSGIServiceRegistration;
+import org.killbill.billing.payment.api.TransactionStatus;
+import org.killbill.billing.payment.core.sm.PaymentStateMachineHelper;
+import org.killbill.billing.payment.core.sm.PluginControlledPaymentAutomatonRunner;
+import org.killbill.billing.payment.core.sm.RetryStateMachineHelper;
+import org.killbill.billing.payment.dao.PaymentDao;
+import org.killbill.billing.payment.plugin.api.PaymentPluginApi;
+import org.killbill.billing.util.cache.CacheControllerDispatcher;
+import org.killbill.billing.util.callcontext.InternalCallContextFactory;
+import org.killbill.billing.util.config.PaymentConfig;
+import org.killbill.billing.util.dao.NonEntityDao;
+import org.killbill.clock.Clock;
+
+import com.google.common.collect.ImmutableList;
+
+/**
+ * Task to find old PENDING transactions and move them into
+ */
+final class PendingTransactionTask extends CompletionTaskBase<Integer> {
+
+    private Janitor janitor;
+    private final List<Integer> itemsForIterations;
+
+    public PendingTransactionTask(final Janitor janitor, final InternalCallContextFactory internalCallContextFactory, final PaymentConfig paymentConfig,
+                                 final NonEntityDao nonEntityDao, final PaymentDao paymentDao, final Clock clock, final PaymentStateMachineHelper paymentStateMachineHelper,
+                                 final RetryStateMachineHelper retrySMHelper, final CacheControllerDispatcher controllerDispatcher, final AccountInternalApi accountInternalApi,
+                                 final PluginControlledPaymentAutomatonRunner pluginControlledPaymentAutomatonRunner, final OSGIServiceRegistration<PaymentPluginApi> pluginRegistry) {
+        super(janitor, internalCallContextFactory, paymentConfig, nonEntityDao, paymentDao, clock, paymentStateMachineHelper, retrySMHelper, controllerDispatcher, accountInternalApi, pluginControlledPaymentAutomatonRunner, pluginRegistry);
+        this.itemsForIterations = ImmutableList.of(new Integer(1));
+    }
+
+    @Override
+    public List<Integer> getItemsForIteration() {
+        return itemsForIterations;
+    }
+
+    @Override
+    public void doIteration(final Integer item) {
+
+        // TODO this is needs to be fixed see- #230
+        int result = paymentDao.failOldPendingTransactions(TransactionStatus.PLUGIN_FAILURE, getCreatedDateBefore(), completionTaskCallContext);
+        if (result > 0) {
+            log.info("Janitor PendingTransactionTask moved " + result + " PENDING payments ->  PLUGIN_FAILURE");
+        }
+    }
+}
diff --git a/payment/src/main/java/org/killbill/billing/payment/core/sm/PaymentAutomatonRunner.java b/payment/src/main/java/org/killbill/billing/payment/core/sm/PaymentAutomatonRunner.java
index eeeaba6..1b2e645 100644
--- a/payment/src/main/java/org/killbill/billing/payment/core/sm/PaymentAutomatonRunner.java
+++ b/payment/src/main/java/org/killbill/billing/payment/core/sm/PaymentAutomatonRunner.java
@@ -21,6 +21,7 @@ import java.math.BigDecimal;
 import java.util.UUID;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
 
 import javax.annotation.Nullable;
 import javax.inject.Inject;
@@ -44,6 +45,7 @@ import org.killbill.billing.osgi.api.OSGIServiceRegistration;
 import org.killbill.billing.payment.api.PaymentApiException;
 import org.killbill.billing.payment.api.PluginProperty;
 import org.killbill.billing.payment.api.TransactionType;
+import org.killbill.billing.payment.control.InvoicePaymentControlPluginApi;
 import org.killbill.billing.payment.dao.PaymentDao;
 import org.killbill.billing.payment.dao.PaymentModelDao;
 import org.killbill.billing.payment.dispatcher.PluginDispatcher;
@@ -57,6 +59,8 @@ import org.killbill.commons.locker.GlobalLocker;
 
 import com.google.common.base.Objects;
 import com.google.common.base.Preconditions;
+import com.google.common.base.Predicate;
+import com.google.common.collect.Iterables;
 import com.google.inject.name.Named;
 
 import static org.killbill.billing.payment.glue.PaymentModule.PLUGIN_EXECUTOR_NAMED;
@@ -94,15 +98,15 @@ public class PaymentAutomatonRunner {
     }
 
     public UUID run(final boolean isApiPayment, final TransactionType transactionType, final Account account, @Nullable final UUID attemptId, @Nullable final UUID paymentMethodId,
-                    @Nullable final UUID paymentId,  @Nullable final UUID transactionId, @Nullable final String paymentExternalKey, final String paymentTransactionExternalKey,
+                    @Nullable final UUID paymentId, @Nullable final UUID transactionId, @Nullable final String paymentExternalKey, final String paymentTransactionExternalKey,
                     @Nullable final BigDecimal amount, @Nullable final Currency currency,
                     final boolean shouldLockAccount, final OperationResult overridePluginOperationResult, final Iterable<PluginProperty> properties,
                     final CallContext callContext, final InternalCallContext internalCallContext) throws PaymentApiException {
 
         final DateTime utcNow = clock.getUTCNow();
 
-        final PaymentStateContext paymentStateContext = new PaymentStateContext(isApiPayment, paymentId, transactionId, attemptId,  paymentExternalKey, paymentTransactionExternalKey, transactionType,
-                                                                                                  account, paymentMethodId, amount, currency, shouldLockAccount, overridePluginOperationResult, properties, internalCallContext, callContext);
+        final PaymentStateContext paymentStateContext = new PaymentStateContext(isApiPayment, paymentId, transactionId, attemptId, paymentExternalKey, paymentTransactionExternalKey, transactionType,
+                                                                                account, paymentMethodId, amount, currency, shouldLockAccount, overridePluginOperationResult, properties, internalCallContext, callContext);
 
         final PaymentAutomatonDAOHelper daoHelper = new PaymentAutomatonDAOHelper(paymentStateContext, utcNow, paymentDao, pluginRegistry, internalCallContext, eventBus, paymentSMHelper);
 
@@ -168,13 +172,14 @@ public class PaymentAutomatonRunner {
                 throw new IllegalStateException("Unsupported transaction type " + transactionType);
         }
 
-        runStateMachineOperation(currentStateName, transactionType, leavingStateCallback, operationCallback, enteringStateCallback);
+        runStateMachineOperation(currentStateName, transactionType, leavingStateCallback, operationCallback, enteringStateCallback, account.getId(), getInvoiceId(properties));
 
         return paymentStateContext.getPaymentId();
     }
 
     protected void runStateMachineOperation(final String initialStateName, final TransactionType transactionType,
-                                            final LeavingStateCallback leavingStateCallback, final OperationCallback operationCallback, final EnteringStateCallback enteringStateCallback) throws PaymentApiException {
+                                            final LeavingStateCallback leavingStateCallback, final OperationCallback operationCallback, final EnteringStateCallback enteringStateCallback,
+                                            final UUID accountId, final String invoiceId) throws PaymentApiException {
         try {
             final StateMachine initialStateMachine = paymentSMHelper.getStateMachineForStateName(initialStateName);
             final State initialState = initialStateMachine.getState(initialStateName);
@@ -188,9 +193,22 @@ public class PaymentAutomatonRunner {
                 throw new PaymentApiException(e, ErrorCode.PAYMENT_INTERNAL_ERROR, Objects.firstNonNull(e.getMessage(), ""));
             } else if (e.getCause() instanceof PaymentApiException) {
                 throw (PaymentApiException) e.getCause();
+            } else if (e.getCause() instanceof TimeoutException) {
+                throw new PaymentApiException(e.getCause(), ErrorCode.PAYMENT_PLUGIN_TIMEOUT, accountId, invoiceId);
             } else {
                 throw new PaymentApiException(e.getCause(), ErrorCode.PAYMENT_INTERNAL_ERROR, Objects.firstNonNull(e.getMessage(), ""));
             }
         }
     }
+
+    private String getInvoiceId(final Iterable<PluginProperty> properties) {
+        final PluginProperty invoiceProperty = Iterables.tryFind(properties, new Predicate<PluginProperty>() {
+            @Override
+            public boolean apply(final PluginProperty input) {
+                return InvoicePaymentControlPluginApi.PROP_IPCD_INVOICE_ID.equals(input.getKey());
+            }
+        }).orNull();
+
+        return invoiceProperty == null || invoiceProperty.getValue() == null ? null : invoiceProperty.getValue().toString();
+    }
 }
diff --git a/payment/src/main/java/org/killbill/billing/payment/core/sm/PaymentEnteringStateCallback.java b/payment/src/main/java/org/killbill/billing/payment/core/sm/PaymentEnteringStateCallback.java
index fd4d928..e31416e 100644
--- a/payment/src/main/java/org/killbill/billing/payment/core/sm/PaymentEnteringStateCallback.java
+++ b/payment/src/main/java/org/killbill/billing/payment/core/sm/PaymentEnteringStateCallback.java
@@ -24,14 +24,10 @@ import org.killbill.automaton.OperationResult;
 import org.killbill.automaton.State;
 import org.killbill.automaton.State.EnteringStateCallback;
 import org.killbill.automaton.State.LeavingStateCallback;
-import org.killbill.billing.account.api.Account;
-import org.killbill.billing.callcontext.InternalCallContext;
 import org.killbill.billing.events.BusInternalEvent;
 import org.killbill.billing.payment.api.DefaultPaymentErrorEvent;
 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.PaymentTransactionModelDao;
 import org.killbill.billing.payment.plugin.api.PaymentTransactionInfoPlugin;
 import org.killbill.bus.api.PersistentBus.EventBusException;
 import org.slf4j.Logger;
@@ -56,12 +52,13 @@ public abstract class PaymentEnteringStateCallback implements EnteringStateCallb
         // If the transaction was not created -- for instance we had an exception in leavingState callback then we bail; if not, then update state:
         if (paymentStateContext.getPaymentTransactionModelDao() != null && paymentStateContext.getPaymentTransactionModelDao().getId() != null) {
             final PaymentTransactionInfoPlugin paymentInfoPlugin = paymentStateContext.getPaymentInfoPlugin();
-            final TransactionStatus paymentStatus = paymentPluginStatusToPaymentStatus(paymentInfoPlugin, operationResult);
-            daoHelper.processPaymentInfoPlugin(paymentStatus, paymentInfoPlugin, newState.getName());
+            final TransactionStatus transactionStatus = paymentPluginStatusToTransactionStatus(paymentInfoPlugin);
+            // The bus event will be posted from the transaction
+            daoHelper.processPaymentInfoPlugin(transactionStatus, paymentInfoPlugin, newState.getName());
         } else if (!paymentStateContext.isApiPayment()) {
             //
             // If there is NO transaction to update (because payment transaction did not occur), then there is something wrong happening (maybe a missing defaultPaymentMethodId, ...)
-            // so, if the does NOT call originates from api then we still want to send a bus event so the system can react to it if needed.
+            // so, if the call does NOT originates from api then we still want to send a bus event so the system can react to it if needed.
             //
             final BusInternalEvent event = new DefaultPaymentErrorEvent(paymentStateContext.getAccount().getId(),
                                                                         null,
@@ -78,31 +75,27 @@ public abstract class PaymentEnteringStateCallback implements EnteringStateCallb
         }
     }
 
-    private TransactionStatus paymentPluginStatusToPaymentStatus(@Nullable final PaymentTransactionInfoPlugin paymentInfoPlugin, final OperationResult operationResult) {
-        if (paymentInfoPlugin == null) {
-            if (OperationResult.EXCEPTION.equals(operationResult)) {
-                // We got an exception during the plugin call
-                return TransactionStatus.PLUGIN_FAILURE;
-            } else {
-                // The plugin completed the call but returned null?! Bad plugin...
-                return TransactionStatus.PLUGIN_FAILURE;
-            }
-        }
+    public static TransactionStatus paymentPluginStatusToTransactionStatus(@Nullable final PaymentTransactionInfoPlugin paymentInfoPlugin) {
 
-        if (paymentInfoPlugin.getStatus() == null) {
-            // The plugin completed the call but returned an incomplete PaymentInfoPlugin?! Bad plugin...
-            return TransactionStatus.UNKNOWN;
+        //
+        // paymentInfoPlugin when we got an exception from the plugin, or if the plugin behaves badly
+        // and decides to return null; in all cases this is seen as a PLUGIN_FAILURE
+        //
+        if (paymentInfoPlugin == null || paymentInfoPlugin.getStatus() == null) {
+                return TransactionStatus.PLUGIN_FAILURE;
         }
 
+        //
+        // The plugin returned a status or it timedout and we added manually a UNKNOWN status to end up here
+        //
         switch (paymentInfoPlugin.getStatus()) {
-            case UNDEFINED:
-                return TransactionStatus.UNKNOWN;
             case PROCESSED:
                 return TransactionStatus.SUCCESS;
             case PENDING:
                 return TransactionStatus.PENDING;
             case ERROR:
                 return TransactionStatus.PAYMENT_FAILURE;
+            case UNDEFINED:
             default:
                 return TransactionStatus.UNKNOWN;
         }
diff --git a/payment/src/main/java/org/killbill/billing/payment/core/sm/PaymentOperation.java b/payment/src/main/java/org/killbill/billing/payment/core/sm/PaymentOperation.java
index e2b2a65..1a44ca5 100644
--- a/payment/src/main/java/org/killbill/billing/payment/core/sm/PaymentOperation.java
+++ b/payment/src/main/java/org/killbill/billing/payment/core/sm/PaymentOperation.java
@@ -83,7 +83,7 @@ public abstract class PaymentOperation extends OperationCallbackBase<PaymentTran
             return new OperationException(realException, OperationResult.FAILURE);
         } else if (e.getCause() instanceof LockFailedException) {
             final String format = String.format("Failed to lock account %s", paymentStateContext.getAccount().getExternalKey());
-            logger.error(String.format(format), e);
+            logger.error(String.format(format));
             return new OperationException(realException, OperationResult.FAILURE);
         } else /* if (e instanceof RuntimeException) */ {
             logger.warn("Plugin call threw an exception for account {}", paymentStateContext.getAccount().getExternalKey(), e);
@@ -93,13 +93,25 @@ public abstract class PaymentOperation extends OperationCallbackBase<PaymentTran
 
     @Override
     protected OperationException wrapTimeoutException(final PaymentStateContext paymentStateContext, final TimeoutException e) {
-        logger.error("Plugin call TIMEOUT for account {}: {}", paymentStateContext.getAccount().getExternalKey(), e.getMessage());
+        logger.error("Plugin call TIMEOUT for account {}", paymentStateContext.getAccount().getExternalKey());
+
+        final PaymentTransactionInfoPlugin paymentInfoPlugin = new DefaultNoOpPaymentInfoPlugin(paymentStateContext.getPaymentId(),
+                                                                                                paymentStateContext.getTransactionId(),
+                                                                                                paymentStateContext.getTransactionType(),
+                                                                                                paymentStateContext.getPaymentTransactionModelDao().getProcessedAmount(),
+                                                                                                paymentStateContext.getPaymentTransactionModelDao().getProcessedCurrency(),
+                                                                                                paymentStateContext.getPaymentTransactionModelDao().getEffectiveDate(),
+                                                                                                paymentStateContext.getPaymentTransactionModelDao().getCreatedDate(),
+                                                                                                PaymentPluginStatus.UNDEFINED,
+                                                                                                null);
+
+        paymentStateContext.setPaymentInfoPlugin(paymentInfoPlugin);
         return new OperationException(e, OperationResult.EXCEPTION);
     }
 
     @Override
     protected OperationException wrapInterruptedException(final PaymentStateContext paymentStateContext, final InterruptedException e) {
-        logger.error("Plugin call was interrupted for account {}: {}", paymentStateContext.getAccount().getExternalKey(), e.getMessage());
+        logger.error("Plugin call was interrupted for account {}", paymentStateContext.getAccount().getExternalKey());
         return new OperationException(e, OperationResult.EXCEPTION);
     }
 
diff --git a/payment/src/main/java/org/killbill/billing/payment/core/sm/PaymentStateMachineHelper.java b/payment/src/main/java/org/killbill/billing/payment/core/sm/PaymentStateMachineHelper.java
index 763ec9c..bc58d65 100644
--- a/payment/src/main/java/org/killbill/billing/payment/core/sm/PaymentStateMachineHelper.java
+++ b/payment/src/main/java/org/killbill/billing/payment/core/sm/PaymentStateMachineHelper.java
@@ -33,6 +33,9 @@ import com.google.common.base.Predicate;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Iterables;
 
+/**
+ * This class needs to know about the payment state machine xml file. All the knowledge about the xml file is encapsulated here.
+ */
 public class PaymentStateMachineHelper {
 
     private static final String BIG_BANG_STATE_MACHINE_NAME = "BIG_BANG";
@@ -44,17 +47,35 @@ public class PaymentStateMachineHelper {
     private static final String VOID_STATE_MACHINE_NAME = "VOID";
     private static final String CHARGEBACK_STATE_MACHINE_NAME = "CHARGEBACK";
 
-
-    private static final String BIG_BANG_INIT_STATE_NAME = "BIG_BANG_INIT";
-    private static final String AUTHORIZE_INIT_STATE_NAME = "AUTH_INIT";
-    private static final String CAPTURE_INIT_STATE_NAME = "CAPTURE_INIT";
-    private static final String PURCHASE_INIT_STATE_NAME = "PURCHASE_INIT";
-    private static final String REFUND_INIT_STATE_NAME = "REFUND_INIT";
-    private static final String CREDIT_INIT_STATE_NAME = "CREDIT_INIT";
-    private static final String VOID_INIT_STATE_NAME = "VOID_INIT";
-    private static final String CHARGEBACK_INIT_STATE_NAME = "CHARGEBACK_INIT";
-
+    private static final String BIG_BANG_INIT = "BIG_BANG_INIT";
+
+    private static final String AUTHORIZE_SUCCESS = "AUTH_SUCCESS";
+    private static final String CAPTURE_SUCCESS = "CAPTURE_SUCCESS";
+    private static final String PURCHASE_SUCCESS = "PURCHASE_SUCCESS";
+    private static final String REFUND_SUCCESS = "REFUND_SUCCESS";
+    private static final String CREDIT_SUCCESS = "CREDIT_SUCCESS";
+    private static final String VOID_SUCCESS = "VOID_SUCCESS";
+    private static final String CHARGEBACK_SUCCESS = "CHARGEBACK_SUCCESS";
+
+    private static final String AUTHORIZE_PENDING = "AUTHORIZE_PENDING";
+
+    private static final String AUTHORIZE_FAILED = "AUTH_FAILED";
+    private static final String CAPTURE_FAILED = "CAPTURE_FAILED";
+    private static final String PURCHASE_FAILED = "PURCHASE_FAILED";
+    private static final String REFUND_FAILED = "REFUND_FAILED";
+    private static final String CREDIT_FAILED = "CREDIT_FAILED";
+    private static final String VOID_FAILED = "VOID_FAILED";
+    private static final String CHARGEBACK_FAILED = "CHARGEBACK_FAILED";
+
+    private static final String AUTH_ERRORED = "AUTH_ERRORED";
+    private static final String CAPTURE_ERRORED = "CAPTURE_ERRORED";
+    private static final String PURCHASE_ERRORED = "PURCHASE_ERRORED";
+    private static final String REFUND_ERRORED = "REFUND_ERRORED";
+    private static final String CREDIT_ERRORED = "CREDIT_ERRORED";
+    private static final String VOID_ERRORED = "VOID_ERRORED";
+    private static final String CHARGEBACK_ERRORED = "CHARGEBACK_ERRORED";
     private final StateMachineConfig stateMachineConfig;
+    private final String[] errorStateNames = {AUTH_ERRORED, CAPTURE_ERRORED, PURCHASE_ERRORED, REFUND_ERRORED, CREDIT_ERRORED, VOID_ERRORED, CHARGEBACK_ERRORED};
 
     @Inject
     public PaymentStateMachineHelper(@javax.inject.Named(PaymentModule.STATE_MACHINE_PAYMENT) final StateMachineConfig stateMachineConfig) {
@@ -62,12 +83,85 @@ public class PaymentStateMachineHelper {
     }
 
     public State getState(final String stateName) throws MissingEntryException {
-        final StateMachine stateMachine  = stateMachineConfig.getStateMachineForState(stateName);
+        final StateMachine stateMachine = stateMachineConfig.getStateMachineForState(stateName);
         return stateMachine.getState(stateName);
     }
 
     public String getInitStateNameForTransaction() {
-        return BIG_BANG_INIT_STATE_NAME;
+        return BIG_BANG_INIT;
+    }
+
+    public String getSuccessfulStateForTransaction(final TransactionType transactionType) {
+        switch (transactionType) {
+            case AUTHORIZE:
+                return AUTHORIZE_SUCCESS;
+            case CAPTURE:
+                return CAPTURE_SUCCESS;
+            case PURCHASE:
+                return PURCHASE_SUCCESS;
+            case REFUND:
+                return REFUND_SUCCESS;
+            case CREDIT:
+                return CREDIT_SUCCESS;
+            case VOID:
+                return VOID_SUCCESS;
+            case CHARGEBACK:
+                return CHARGEBACK_SUCCESS;
+            default:
+                throw new IllegalStateException("Unsupported transaction type " + transactionType);
+        }
+    }
+
+    public String getPendingStateForTransaction(final TransactionType transactionType) {
+        switch (transactionType) {
+            case AUTHORIZE:
+                return AUTHORIZE_PENDING;
+            default:
+                throw new IllegalStateException("Unsupported transaction type " + transactionType);
+        }
+    }
+
+
+    public String getErroredStateForTransaction(final TransactionType transactionType) {
+        switch (transactionType) {
+            case AUTHORIZE:
+                return AUTH_ERRORED;
+            case CAPTURE:
+                return CAPTURE_ERRORED;
+            case PURCHASE:
+                return PURCHASE_ERRORED;
+            case REFUND:
+                return REFUND_ERRORED;
+            case CREDIT:
+                return CREDIT_ERRORED;
+            case VOID:
+                return VOID_ERRORED;
+            case CHARGEBACK:
+                return CHARGEBACK_ERRORED;
+            default:
+                throw new IllegalStateException("Unsupported transaction type " + transactionType);
+        }
+    }
+
+    public String getFailureStateForTransaction(final TransactionType transactionType) {
+        switch (transactionType) {
+            case AUTHORIZE:
+                return AUTHORIZE_FAILED;
+            case CAPTURE:
+                return CAPTURE_FAILED;
+            case PURCHASE:
+                return PURCHASE_FAILED;
+            case REFUND:
+                return REFUND_FAILED;
+            case CREDIT:
+                return CREDIT_FAILED;
+            case VOID:
+                return VOID_FAILED;
+            case CHARGEBACK:
+                return CHARGEBACK_FAILED;
+            default:
+                throw new IllegalStateException("Unsupported transaction type " + transactionType);
+        }
     }
 
     public StateMachine getStateMachineForStateName(final String stateName) throws MissingEntryException {
@@ -118,4 +212,9 @@ public class PaymentStateMachineHelper {
         }).orNull();
         return transition != null ? transition.getFinalState() : null;
     }
+
+    public String[] getErroredStateNames() {
+        return errorStateNames;
+    }
+
 }
diff --git a/payment/src/main/java/org/killbill/billing/payment/core/sm/RetryOperationCallback.java b/payment/src/main/java/org/killbill/billing/payment/core/sm/RetryOperationCallback.java
index 0e44d5c..580e180 100644
--- a/payment/src/main/java/org/killbill/billing/payment/core/sm/RetryOperationCallback.java
+++ b/payment/src/main/java/org/killbill/billing/payment/core/sm/RetryOperationCallback.java
@@ -157,7 +157,7 @@ public abstract class RetryOperationCallback extends OperationCallbackBase<Payme
             return (OperationException) e.getCause();
         } else if (e.getCause() instanceof LockFailedException) {
             final String format = String.format("Failed to lock account %s", paymentStateContext.getAccount().getExternalKey());
-            logger.error(String.format(format), e);
+            logger.error(String.format(format));
             return new OperationException(e, getOperationResultOnException(paymentStateContext));
         } else /* most probably RuntimeException */ {
             logger.warn("RetryOperationCallback failed for account {}", paymentStateContext.getAccount().getExternalKey(), e);
@@ -167,13 +167,13 @@ public abstract class RetryOperationCallback extends OperationCallbackBase<Payme
 
     @Override
     protected OperationException wrapTimeoutException(final PaymentStateContext paymentStateContext, final TimeoutException e) {
-        logger.error("RetryOperationCallback call TIMEOUT for account {}: {}", paymentStateContext.getAccount().getExternalKey(), e.getMessage());
+        logger.warn("RetryOperationCallback call TIMEOUT for account {}", paymentStateContext.getAccount().getExternalKey());
         return new OperationException(e, getOperationResultOnException(paymentStateContext));
     }
 
     @Override
     protected OperationException wrapInterruptedException(final PaymentStateContext paymentStateContext, final InterruptedException e) {
-        logger.error("RetryOperationCallback call was interrupted for account {}: {}", paymentStateContext.getAccount().getExternalKey(), e.getMessage());
+        logger.error("RetryOperationCallback call was interrupted for account {}", paymentStateContext.getAccount().getExternalKey());
         return new OperationException(e, getOperationResultOnException(paymentStateContext));
     }
 
diff --git a/payment/src/main/java/org/killbill/billing/payment/core/sm/RetryStateMachineHelper.java b/payment/src/main/java/org/killbill/billing/payment/core/sm/RetryStateMachineHelper.java
index 30bb1ed..0f261cc 100644
--- a/payment/src/main/java/org/killbill/billing/payment/core/sm/RetryStateMachineHelper.java
+++ b/payment/src/main/java/org/killbill/billing/payment/core/sm/RetryStateMachineHelper.java
@@ -37,6 +37,7 @@ public class RetryStateMachineHelper {
     private static final String INIT_STATE_NAME = "INIT";
     private static final String RETRIED_STATE_NAME = "RETRIED";
 
+
     private final StateMachineConfig retryStateMachineConfig;
     private final StateMachine retryStateMachine;
     private final Operation retryOperation;
diff --git a/payment/src/main/java/org/killbill/billing/payment/dao/DefaultPaymentDao.java b/payment/src/main/java/org/killbill/billing/payment/dao/DefaultPaymentDao.java
index 6423866..6719247 100644
--- a/payment/src/main/java/org/killbill/billing/payment/dao/DefaultPaymentDao.java
+++ b/payment/src/main/java/org/killbill/billing/payment/dao/DefaultPaymentDao.java
@@ -26,6 +26,7 @@ import java.util.UUID;
 
 import javax.annotation.Nullable;
 import javax.inject.Inject;
+import javax.management.ImmutableDescriptor;
 
 import org.joda.time.DateTime;
 import org.killbill.billing.callcontext.InternalCallContext;
@@ -60,6 +61,7 @@ import com.google.common.base.Function;
 import com.google.common.base.Preconditions;
 import com.google.common.base.Predicate;
 import com.google.common.collect.Collections2;
+import com.google.common.collect.ImmutableList;
 
 public class DefaultPaymentDao implements PaymentDao {
 
@@ -165,6 +167,8 @@ public class DefaultPaymentDao implements PaymentDao {
                             return input.getId().toString();
                         }
                     });
+
+
                     return transactional.failOldPendingTransactions(oldPendingTransactionIds, TransactionStatus.PAYMENT_FAILURE.toString(), context);
                 }
                 return 0;
@@ -322,6 +326,16 @@ public class DefaultPaymentDao implements PaymentDao {
     }
 
     @Override
+    public List<PaymentModelDao> getPaymentsByStates(final String[] states, final DateTime createdBeforeDate, final DateTime createdAfterDate, final int limit, final InternalTenantContext context) {
+        return transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<List<PaymentModelDao>>() {
+            @Override
+            public List<PaymentModelDao> inTransaction(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory) throws Exception {
+                return entitySqlDaoWrapperFactory.become(PaymentSqlDao.class).getPaymentsByStates(ImmutableList.copyOf(states), createdBeforeDate.toDate(), createdAfterDate.toDate(), context, limit);
+            }
+        });
+    }
+
+    @Override
     public List<PaymentTransactionModelDao> getTransactionsForAccount(final UUID accountId, final InternalTenantContext context) {
         Preconditions.checkArgument(context.getAccountRecordId() != null);
         return transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<List<PaymentTransactionModelDao>>() {
diff --git a/payment/src/main/java/org/killbill/billing/payment/dao/PaymentDao.java b/payment/src/main/java/org/killbill/billing/payment/dao/PaymentDao.java
index 8055938..1119b85 100644
--- a/payment/src/main/java/org/killbill/billing/payment/dao/PaymentDao.java
+++ b/payment/src/main/java/org/killbill/billing/payment/dao/PaymentDao.java
@@ -30,13 +30,13 @@ import org.killbill.billing.util.entity.Pagination;
 
 public interface PaymentDao {
 
-    public int failOldPendingTransactions(final TransactionStatus newTransactionStatus, final DateTime createdBeforeDate, final InternalCallContext context);
+    public int failOldPendingTransactions(TransactionStatus newTransactionStatus, DateTime createdBeforeDate, InternalCallContext context);
 
     public PaymentAttemptModelDao insertPaymentAttemptWithProperties(PaymentAttemptModelDao attempt, InternalCallContext context);
 
     public void updatePaymentAttempt(UUID paymentAttemptId, UUID transactionId, String state, InternalCallContext context);
 
-    public List<PaymentAttemptModelDao> getPaymentAttemptsByState(String stateName, final DateTime createdBeforeDate, InternalTenantContext context);
+    public List<PaymentAttemptModelDao> getPaymentAttemptsByState(String stateName, DateTime createdBeforeDate, InternalTenantContext context);
 
     public List<PaymentAttemptModelDao> getPaymentAttempts(String paymentExternalKey, InternalTenantContext context);
 
@@ -54,7 +54,7 @@ public interface PaymentDao {
 
     public PaymentTransactionModelDao updatePaymentWithNewTransaction(UUID paymentId, PaymentTransactionModelDao paymentTransaction, InternalCallContext context);
 
-    public void updatePaymentAndTransactionOnCompletion(UUID accountId, UUID paymentId, final TransactionType transactionType, String currentPaymentStateName, String lastPaymentSuccessStateName, UUID transactionId,
+    public void updatePaymentAndTransactionOnCompletion(UUID accountId, UUID paymentId, TransactionType transactionType, String currentPaymentStateName, String lastPaymentSuccessStateName, UUID transactionId,
                                                         TransactionStatus paymentStatus, BigDecimal processedAmount, Currency processedCurrency,
                                                         String gatewayErrorCode, String gatewayErrorMsg, InternalCallContext context);
 
@@ -64,6 +64,8 @@ public interface PaymentDao {
 
     public List<PaymentModelDao> getPaymentsForAccount(UUID accountId, InternalTenantContext context);
 
+    public List<PaymentModelDao> getPaymentsByStates(String [] states, DateTime createdBeforeDate, DateTime createdAfterDate, int limit, InternalTenantContext context);
+
     public List<PaymentTransactionModelDao> getTransactionsForAccount(UUID accountId, InternalTenantContext context);
 
     public List<PaymentTransactionModelDao> getTransactionsForPayment(UUID paymentId, InternalTenantContext context);
diff --git a/payment/src/main/java/org/killbill/billing/payment/dao/PaymentSqlDao.java b/payment/src/main/java/org/killbill/billing/payment/dao/PaymentSqlDao.java
index 432d130..4d8dd0c 100644
--- a/payment/src/main/java/org/killbill/billing/payment/dao/PaymentSqlDao.java
+++ b/payment/src/main/java/org/killbill/billing/payment/dao/PaymentSqlDao.java
@@ -16,7 +16,10 @@
 
 package org.killbill.billing.payment.dao;
 
+import java.util.Collection;
+import java.util.Date;
 import java.util.Iterator;
+import java.util.List;
 
 import org.killbill.billing.callcontext.InternalCallContext;
 import org.killbill.billing.callcontext.InternalTenantContext;
@@ -30,6 +33,7 @@ import org.skife.jdbi.v2.sqlobject.Bind;
 import org.skife.jdbi.v2.sqlobject.BindBean;
 import org.skife.jdbi.v2.sqlobject.SqlQuery;
 import org.skife.jdbi.v2.sqlobject.SqlUpdate;
+import org.skife.jdbi.v2.sqlobject.customizers.Define;
 
 @EntitySqlDaoStringTemplate
 public interface PaymentSqlDao extends EntitySqlDao<PaymentModelDao, Payment> {
@@ -56,6 +60,14 @@ public interface PaymentSqlDao extends EntitySqlDao<PaymentModelDao, Payment> {
     @SqlQuery
     public PaymentModelDao getPaymentByExternalKey(@Bind("externalKey") final String externalKey,
                                                    @BindBean final InternalTenantContext context);
+
+    @SqlQuery
+    public List<PaymentModelDao> getPaymentsByStates(@StateCollectionBinder final Collection<String> states,
+                                                     @Bind("createdBeforeDate") final Date createdBeforeDate,
+                                                     @Bind("createdAfterDate") final Date createdAfterDate,
+                                                     @BindBean final InternalTenantContext context,
+                                                     @Bind("limit") final int limit);
+
     @SqlQuery
     @SmartFetchSize(shouldStream = true)
     public Iterator<PaymentModelDao> getByPluginName(@Bind("pluginName") final String pluginName,
diff --git a/payment/src/main/java/org/killbill/billing/payment/dao/StateCollectionBinder.java b/payment/src/main/java/org/killbill/billing/payment/dao/StateCollectionBinder.java
new file mode 100644
index 0000000..8aac0c9
--- /dev/null
+++ b/payment/src/main/java/org/killbill/billing/payment/dao/StateCollectionBinder.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2014 Groupon, Inc
+ * Copyright 2014 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.payment.dao;
+
+import java.lang.annotation.Annotation;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import java.util.Collection;
+
+import org.killbill.billing.payment.dao.StateCollectionBinder.StateCollectionBinderFactory;
+import org.skife.jdbi.v2.SQLStatement;
+import org.skife.jdbi.v2.sqlobject.Binder;
+import org.skife.jdbi.v2.sqlobject.BinderFactory;
+import org.skife.jdbi.v2.sqlobject.BindingAnnotation;
+
+
+@BindingAnnotation(StateCollectionBinderFactory.class)
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.PARAMETER})
+public @interface StateCollectionBinder {
+
+    public static class StateCollectionBinderFactory implements BinderFactory {
+
+        @Override
+        public Binder build(Annotation annotation) {
+            return new Binder<StateCollectionBinder, Collection<String>>() {
+
+                @Override
+                public void bind(SQLStatement<?> query, StateCollectionBinder bind, Collection<String> states) {
+                    query.define("states", states);
+
+                    int idx = 0;
+                    for (String state : states) {
+                        query.bind("state_" + idx, state);
+                        idx++;
+                    }
+                }
+            };
+        }
+    }
+}
diff --git a/payment/src/main/java/org/killbill/billing/payment/glue/DefaultPaymentService.java b/payment/src/main/java/org/killbill/billing/payment/glue/DefaultPaymentService.java
index 2a8e0aa..7dc8b60 100644
--- a/payment/src/main/java/org/killbill/billing/payment/glue/DefaultPaymentService.java
+++ b/payment/src/main/java/org/killbill/billing/payment/glue/DefaultPaymentService.java
@@ -22,7 +22,7 @@ import org.killbill.billing.payment.api.PaymentApi;
 import org.killbill.billing.payment.api.PaymentService;
 import org.killbill.billing.payment.bus.InvoiceHandler;
 import org.killbill.billing.payment.control.PaymentTagHandler;
-import org.killbill.billing.payment.core.Janitor;
+import org.killbill.billing.payment.core.janitor.Janitor;
 import org.killbill.billing.payment.retry.DefaultRetryService;
 import org.killbill.billing.platform.api.LifecycleHandlerType;
 import org.killbill.billing.platform.api.LifecycleHandlerType.LifecycleLevel;
diff --git a/payment/src/main/java/org/killbill/billing/payment/glue/PaymentModule.java b/payment/src/main/java/org/killbill/billing/payment/glue/PaymentModule.java
index f5a8722..23a7581 100644
--- a/payment/src/main/java/org/killbill/billing/payment/glue/PaymentModule.java
+++ b/payment/src/main/java/org/killbill/billing/payment/glue/PaymentModule.java
@@ -37,7 +37,7 @@ import org.killbill.billing.payment.api.PaymentService;
 import org.killbill.billing.payment.bus.InvoiceHandler;
 import org.killbill.billing.payment.control.PaymentTagHandler;
 import org.killbill.billing.payment.control.dao.InvoicePaymentControlDao;
-import org.killbill.billing.payment.core.Janitor;
+import org.killbill.billing.payment.core.janitor.Janitor;
 import org.killbill.billing.payment.core.PaymentGatewayProcessor;
 import org.killbill.billing.payment.core.PaymentMethodProcessor;
 import org.killbill.billing.payment.core.PaymentProcessor;
diff --git a/payment/src/main/resources/org/killbill/billing/payment/dao/PaymentSqlDao.sql.stg b/payment/src/main/resources/org/killbill/billing/payment/dao/PaymentSqlDao.sql.stg
index 51ca239..3a96d8f 100644
--- a/payment/src/main/resources/org/killbill/billing/payment/dao/PaymentSqlDao.sql.stg
+++ b/payment/src/main/resources/org/killbill/billing/payment/dao/PaymentSqlDao.sql.stg
@@ -101,3 +101,15 @@ join payment_methods pm on pm.id = t.payment_method_id
 where pm.plugin_name = :pluginName
 ;
 >>
+
+getPaymentsByStates(states) ::= <<
+select
+<allTableFields("t.")>
+from <tableName()> t
+where
+created_date >= :createdAfterDate
+and created_date \<= :createdBeforeDate
+and state_name in (<states: {state | :state_<i0>}; separator="," >)
+limit :limit
+;
+>>
diff --git a/payment/src/test/java/org/killbill/billing/payment/dao/MockPaymentDao.java b/payment/src/test/java/org/killbill/billing/payment/dao/MockPaymentDao.java
index 2207741..2adef8b 100644
--- a/payment/src/test/java/org/killbill/billing/payment/dao/MockPaymentDao.java
+++ b/payment/src/test/java/org/killbill/billing/payment/dao/MockPaymentDao.java
@@ -220,6 +220,11 @@ public class MockPaymentDao implements PaymentDao {
     }
 
     @Override
+    public List<PaymentModelDao> getPaymentsByStates(final String[] states, final DateTime createdBeforeDate, final DateTime createdAfterDate, final int limit, final InternalTenantContext context) {
+        return null;
+    }
+
+    @Override
     public List<PaymentTransactionModelDao> getTransactionsForAccount(final UUID accountId, final InternalTenantContext context) {
         synchronized (this) {
             return ImmutableList.copyOf(Iterables.filter(transactions.values(), new Predicate<PaymentTransactionModelDao>() {
diff --git a/payment/src/test/java/org/killbill/billing/payment/dao/TestPaymentDao.java b/payment/src/test/java/org/killbill/billing/payment/dao/TestPaymentDao.java
index d8e3b15..69045eb 100644
--- a/payment/src/test/java/org/killbill/billing/payment/dao/TestPaymentDao.java
+++ b/payment/src/test/java/org/killbill/billing/payment/dao/TestPaymentDao.java
@@ -165,7 +165,7 @@ public class TestPaymentDao extends PaymentTestSuiteWithEmbeddedDB {
         assertEquals(transactions.size(), 2);
 
         paymentDao.updatePaymentAndTransactionOnCompletion(accountId, savedPayment.getId(), savedTransactionModelDao2.getTransactionType(), "AUTH_ABORTED", "AUTH_SUCCESS", transactionModelDao2.getId(), TransactionStatus.SUCCESS,
-                                                                 BigDecimal.ONE, Currency.USD, null, "nothing", internalCallContext);
+                                                           BigDecimal.ONE, Currency.USD, null, "nothing", internalCallContext);
 
         final PaymentModelDao savedPayment4 = paymentDao.getPayment(savedPayment.getId(), internalCallContext);
         assertEquals(savedPayment4.getId(), paymentModelDao.getId());
@@ -188,7 +188,7 @@ public class TestPaymentDao extends PaymentTestSuiteWithEmbeddedDB {
         assertEquals(savedTransactionModelDao4.getGatewayErrorMsg(), "nothing");
 
         paymentDao.updatePaymentAndTransactionOnCompletion(accountId, savedPayment.getId(), savedTransactionModelDao2.getTransactionType(), "AUTH_ABORTED", null, transactionModelDao2.getId(), TransactionStatus.SUCCESS,
-                                                                 BigDecimal.ONE, Currency.USD, null, "nothing", internalCallContext);
+                                                           BigDecimal.ONE, Currency.USD, null, "nothing", internalCallContext);
 
         final PaymentModelDao savedPayment4Again = paymentDao.getPayment(savedPayment.getId(), internalCallContext);
         assertEquals(savedPayment4Again.getId(), paymentModelDao.getId());
@@ -196,7 +196,7 @@ public class TestPaymentDao extends PaymentTestSuiteWithEmbeddedDB {
         assertEquals(savedPayment4Again.getLastSuccessStateName(), "AUTH_SUCCESS");
 
         paymentDao.updatePaymentAndTransactionOnCompletion(accountId, savedPayment.getId(), savedTransactionModelDao2.getTransactionType(), "AUTH_ABORTED", "AUTH_SUCCESS", transactionModelDao2.getId(), TransactionStatus.SUCCESS,
-                                                                 BigDecimal.ONE, Currency.USD, null, "nothing", internalCallContext);
+                                                           BigDecimal.ONE, Currency.USD, null, "nothing", internalCallContext);
 
         final PaymentModelDao savedPayment4Final = paymentDao.getPayment(savedPayment.getId(), internalCallContext);
         assertEquals(savedPayment4Final.getId(), paymentModelDao.getId());
@@ -285,11 +285,10 @@ public class TestPaymentDao extends PaymentTestSuiteWithEmbeddedDB {
         clock.addDays(1);
         final DateTime newTime = clock.getUTCNow();
 
-
         final InternalCallContext internalCallContextWithNewTime = new InternalCallContext(InternalCallContextFactory.INTERNAL_TENANT_RECORD_ID, 1687L, UUID.randomUUID(),
-                                                                                        UUID.randomUUID().toString(), CallOrigin.TEST,
-                                                                                        UserType.TEST, "Testing", "This is a test",
-                                                                                        newTime, newTime);
+                                                                                           UUID.randomUUID().toString(), CallOrigin.TEST,
+                                                                                           UserType.TEST, "Testing", "This is a test",
+                                                                                           newTime, newTime);
 
         final PaymentTransactionModelDao transaction4 = new PaymentTransactionModelDao(initialTime, initialTime, null, transactionExternalKey4,
                                                                                        paymentModelDao.getId(), TransactionType.AUTHORIZE, newTime,
@@ -297,18 +296,20 @@ public class TestPaymentDao extends PaymentTestSuiteWithEmbeddedDB {
                                                                                        "pending", "");
         paymentDao.updatePaymentWithNewTransaction(paymentModelDao.getId(), transaction4, internalCallContextWithNewTime);
 
-
         final List<PaymentTransactionModelDao> result = getPendingTransactions(paymentModelDao.getId());
         Assert.assertEquals(result.size(), 3);
 
-
         paymentDao.failOldPendingTransactions(TransactionStatus.PAYMENT_FAILURE, newTime, internalCallContext);
 
         final List<PaymentTransactionModelDao> result2 = getPendingTransactions(paymentModelDao.getId());
         Assert.assertEquals(result2.size(), 1);
 
         // Just to guarantee that next clock.getUTCNow() > newTime
-        try { Thread.sleep(1000); } catch (InterruptedException e) {};
+        try {
+            Thread.sleep(1000);
+        } catch (InterruptedException e) {
+        }
+        ;
 
         paymentDao.failOldPendingTransactions(TransactionStatus.PAYMENT_FAILURE, clock.getUTCNow(), internalCallContextWithNewTime);
 
@@ -317,8 +318,146 @@ public class TestPaymentDao extends PaymentTestSuiteWithEmbeddedDB {
 
     }
 
+    @Test(groups = "slow")
+    public void testPaymentByStates() {
+
+        final UUID paymentMethodId = UUID.randomUUID();
+        final UUID accountId = UUID.randomUUID();
+        final String externalKey1 = "XXhhhhooo1";
+        final String transactionExternalKey1 = "transactionXX1";
+
+        final String externalKey2 = "XXhhhhooo2";
+        final String transactionExternalKey2 = "transactionXX2";
+
+        final String externalKey3 = "XXhhhhooo3";
+        final String transactionExternalKey3 = "transactionXX3";
+
+        final String externalKey4 = "XXhhhhooo4";
+        final String transactionExternalKey4 = "transactionXX4";
+
+        final String externalKey5 = "XXhhhhooo5";
+        final String transactionExternalKey5 = "transactionXX5";
+
+        final DateTime createdAfterDate = clock.getUTCNow().minusDays(10);
+        final DateTime createdBeforeDate = clock.getUTCNow().minusDays(1);
+
+        // Right before createdAfterDate, so should not be returned
+        final DateTime createdDate1 = createdAfterDate.minusHours(1);
+        final PaymentModelDao paymentModelDao1 = new PaymentModelDao(createdDate1, createdDate1, accountId, paymentMethodId, externalKey1);
+        paymentModelDao1.setStateName("AUTH_ERRORED");
+        final PaymentTransactionModelDao transaction1 = new PaymentTransactionModelDao(createdDate1, createdDate1, null, transactionExternalKey1,
+                                                                                       paymentModelDao1.getId(), TransactionType.AUTHORIZE, createdDate1,
+                                                                                       TransactionStatus.UNKNOWN, BigDecimal.TEN, Currency.AED,
+                                                                                       "unknown", "");
+
+        final InternalCallContext context1 = new InternalCallContext(internalCallContext.getTenantRecordId(),
+                                                                     internalCallContext.getAccountRecordId(),
+                                                                     internalCallContext.getUserToken(),
+                                                                     internalCallContext.getCreatedBy(),
+                                                                     internalCallContext.getCallOrigin(),
+                                                                     internalCallContext.getContextUserType(),
+                                                                     internalCallContext.getReasonCode(),
+                                                                     internalCallContext.getComments(),
+                                                                     createdDate1,
+                                                                     createdDate1);
+        paymentDao.insertPaymentWithFirstTransaction(paymentModelDao1, transaction1, context1);
+
+
+        // Right after createdAfterDate, so it should  be returned
+        final DateTime createdDate2 = createdAfterDate.plusHours(1);
+        final PaymentModelDao paymentModelDao2 = new PaymentModelDao(createdDate2, createdDate2, accountId, paymentMethodId, externalKey2);
+        paymentModelDao2.setStateName("CAPTURE_ERRORED");
+        final PaymentTransactionModelDao transaction2 = new PaymentTransactionModelDao(createdDate2, createdDate2, null, transactionExternalKey2,
+                                                                                       paymentModelDao2.getId(), TransactionType.AUTHORIZE, createdDate2,
+                                                                                       TransactionStatus.UNKNOWN, BigDecimal.TEN, Currency.AED,
+                                                                                       "unknown", "");
+
+        final InternalCallContext context2 = new InternalCallContext(internalCallContext.getTenantRecordId(),
+                                                                     internalCallContext.getAccountRecordId(),
+                                                                     internalCallContext.getUserToken(),
+                                                                     internalCallContext.getCreatedBy(),
+                                                                     internalCallContext.getCallOrigin(),
+                                                                     internalCallContext.getContextUserType(),
+                                                                     internalCallContext.getReasonCode(),
+                                                                     internalCallContext.getComments(),
+                                                                     createdDate2,
+                                                                     createdDate2);
+        paymentDao.insertPaymentWithFirstTransaction(paymentModelDao2, transaction2, context2);
+
+        // Right before createdBeforeDate, so it should be returned
+        final DateTime createdDate3 = createdBeforeDate.minusDays(1);
+        final PaymentModelDao paymentModelDao3 = new PaymentModelDao(createdDate3, createdDate3, accountId, paymentMethodId, externalKey3);
+        paymentModelDao3.setStateName("CAPTURE_ERRORED");
+        final PaymentTransactionModelDao transaction3 = new PaymentTransactionModelDao(createdDate3, createdDate3, null, transactionExternalKey3,
+                                                                                       paymentModelDao3.getId(), TransactionType.AUTHORIZE, createdDate3,
+                                                                                       TransactionStatus.UNKNOWN, BigDecimal.TEN, Currency.AED,
+                                                                                       "unknown", "");
+
+        final InternalCallContext context3 = new InternalCallContext(internalCallContext.getTenantRecordId(),
+                                                                     internalCallContext.getAccountRecordId(),
+                                                                     internalCallContext.getUserToken(),
+                                                                     internalCallContext.getCreatedBy(),
+                                                                     internalCallContext.getCallOrigin(),
+                                                                     internalCallContext.getContextUserType(),
+                                                                     internalCallContext.getReasonCode(),
+                                                                     internalCallContext.getComments(),
+                                                                     createdDate3,
+                                                                     createdDate3);
+
+        paymentDao.insertPaymentWithFirstTransaction(paymentModelDao3, transaction3, context3);
+
+
+        // Right before createdBeforeDate but with a SUCCESS state so it should NOT be returned
+        final DateTime createdDate4 = createdBeforeDate.minusDays(1);
+        final PaymentModelDao paymentModelDao4 = new PaymentModelDao(createdDate4, createdDate4, accountId, paymentMethodId, externalKey4);
+        paymentModelDao4.setStateName("CAPTURE_SUCCESS");
+        final PaymentTransactionModelDao transaction4 = new PaymentTransactionModelDao(createdDate4, createdDate4, null, transactionExternalKey4,
+                                                                                       paymentModelDao4.getId(), TransactionType.AUTHORIZE, createdDate4,
+                                                                                       TransactionStatus.UNKNOWN, BigDecimal.TEN, Currency.AED,
+                                                                                       "unknown", "");
+
+        final InternalCallContext context4 = new InternalCallContext(internalCallContext.getTenantRecordId(),
+                                                                     internalCallContext.getAccountRecordId(),
+                                                                     internalCallContext.getUserToken(),
+                                                                     internalCallContext.getCreatedBy(),
+                                                                     internalCallContext.getCallOrigin(),
+                                                                     internalCallContext.getContextUserType(),
+                                                                     internalCallContext.getReasonCode(),
+                                                                     internalCallContext.getComments(),
+                                                                     createdDate4,
+                                                                     createdDate4);
+
+        paymentDao.insertPaymentWithFirstTransaction(paymentModelDao4, transaction4, context4);
+
+        // Right after createdBeforeDate, so it should NOT be returned
+        final DateTime createdDate5 = createdBeforeDate.plusDays(1);
+        final PaymentModelDao paymentModelDao5 = new PaymentModelDao(createdDate5, createdDate5, accountId, paymentMethodId, externalKey5);
+        paymentModelDao5.setStateName("CAPTURE_ERRORED");
+        final PaymentTransactionModelDao transaction5 = new PaymentTransactionModelDao(createdDate5, createdDate5, null, transactionExternalKey5,
+                                                                                       paymentModelDao5.getId(), TransactionType.AUTHORIZE, createdDate5,
+                                                                                       TransactionStatus.UNKNOWN, BigDecimal.TEN, Currency.AED,
+                                                                                       "unknown", "");
+
+        final InternalCallContext context5 = new InternalCallContext(internalCallContext.getTenantRecordId(),
+                                                                     internalCallContext.getAccountRecordId(),
+                                                                     internalCallContext.getUserToken(),
+                                                                     internalCallContext.getCreatedBy(),
+                                                                     internalCallContext.getCallOrigin(),
+                                                                     internalCallContext.getContextUserType(),
+                                                                     internalCallContext.getReasonCode(),
+                                                                     internalCallContext.getComments(),
+                                                                     createdDate5,
+                                                                     createdDate5);
+
+        paymentDao.insertPaymentWithFirstTransaction(paymentModelDao5, transaction5, context5);
+
+        final String[] errorStates = {"AUTH_ERRORED", "CAPTURE_ERRORED", "REFUND_ERRORED", "CREDIT_ERRORED"};
+        final List<PaymentModelDao> result = paymentDao.getPaymentsByStates(errorStates, createdBeforeDate, createdAfterDate, 10, internalCallContext);
+        assertEquals(result.size(), 2);
+    }
+
     private List<PaymentTransactionModelDao> getPendingTransactions(final UUID paymentId) {
-        final List<PaymentTransactionModelDao> total =  paymentDao.getTransactionsForPayment(paymentId, internalCallContext);
+        final List<PaymentTransactionModelDao> total = paymentDao.getTransactionsForPayment(paymentId, internalCallContext);
         return ImmutableList.copyOf(Iterables.filter(total, new Predicate<PaymentTransactionModelDao>() {
             @Override
             public boolean apply(final PaymentTransactionModelDao input) {
diff --git a/payment/src/test/java/org/killbill/billing/payment/TestJanitor.java b/payment/src/test/java/org/killbill/billing/payment/TestJanitor.java
index 422ccfd..3281965 100644
--- a/payment/src/test/java/org/killbill/billing/payment/TestJanitor.java
+++ b/payment/src/test/java/org/killbill/billing/payment/TestJanitor.java
@@ -37,9 +37,8 @@ import org.killbill.billing.payment.api.PluginProperty;
 import org.killbill.billing.payment.api.TransactionStatus;
 import org.killbill.billing.payment.api.TransactionType;
 import org.killbill.billing.payment.control.InvoicePaymentControlPluginApi;
-import org.killbill.billing.payment.core.Janitor;
+import org.killbill.billing.payment.core.janitor.Janitor;
 import org.killbill.billing.payment.dao.PaymentAttemptModelDao;
-import org.killbill.billing.payment.glue.TestPaymentModuleWithEmbeddedDB;
 import org.killbill.billing.payment.provider.MockPaymentProviderPlugin;
 import org.killbill.billing.platform.api.KillbillConfigSource;
 import org.killbill.bus.api.PersistentBus.EventBusException;
@@ -50,9 +49,7 @@ import org.testng.annotations.BeforeMethod;
 import org.testng.annotations.Test;
 
 import com.google.common.collect.ImmutableMap;
-import com.google.inject.Guice;
 import com.google.inject.Inject;
-import com.google.inject.Injector;
 
 import static org.testng.Assert.assertEquals;
 

pom.xml 4(+2 -2)

diff --git a/pom.xml b/pom.xml
index fd19ffc..6a1a29f 100644
--- a/pom.xml
+++ b/pom.xml
@@ -20,10 +20,10 @@
     <parent>
         <artifactId>killbill-oss-parent</artifactId>
         <groupId>org.kill-bill.billing</groupId>
-        <version>0.7.23-SNAPSHOT</version>
+        <version>0.7.26</version>
     </parent>
     <artifactId>killbill</artifactId>
-    <version>0.11.10-SNAPSHOT</version>
+    <version>0.11.11-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 ee9ae1c..f91190e 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.11.10-SNAPSHOT</version>
+        <version>0.11.11-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>killbill-profiles-killbill</artifactId>
diff --git a/profiles/killbill/src/main/java/org/killbill/billing/server/security/KillbillJdbcRealm.java b/profiles/killbill/src/main/java/org/killbill/billing/server/security/KillbillJdbcRealm.java
index 1ff2198..5b0ead6 100644
--- a/profiles/killbill/src/main/java/org/killbill/billing/server/security/KillbillJdbcRealm.java
+++ b/profiles/killbill/src/main/java/org/killbill/billing/server/security/KillbillJdbcRealm.java
@@ -74,7 +74,7 @@ public class KillbillJdbcRealm extends JdbcRealm {
 
     private void configureDataSource() {
         final DataSource realDataSource = new DataSourceProvider(config).get();
-        final DataSource dataSource = new ReferenceableDataSourceSpy<DataSource>(realDataSource, SHIRO_DATA_SOURCE_ID);
+        final DataSource dataSource = new ReferenceableDataSourceSpy(realDataSource, SHIRO_DATA_SOURCE_ID);
         setDataSource(dataSource);
     }
 }
diff --git a/profiles/killpay/pom.xml b/profiles/killpay/pom.xml
index 913b7ab..0061180 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.11.10-SNAPSHOT</version>
+        <version>0.11.11-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 7e8d594..e71e4d8 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.11.10-SNAPSHOT</version>
+        <version>0.11.11-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>killbill-profiles</artifactId>
diff --git a/subscription/pom.xml b/subscription/pom.xml
index 1b46563..b696a0e 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.11.10-SNAPSHOT</version>
+        <version>0.11.11-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 13b6f60..0ea3415 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.11.10-SNAPSHOT</version>
+        <version>0.11.11-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 0d62d1a..e08d78a 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.11.10-SNAPSHOT</version>
+        <version>0.11.11-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>killbill-usage</artifactId>
diff --git a/usage/src/main/java/org/killbill/billing/usage/api/user/DefaultRolledUpUnit.java b/usage/src/main/java/org/killbill/billing/usage/api/user/DefaultRolledUpUnit.java
new file mode 100644
index 0000000..50c7ac3
--- /dev/null
+++ b/usage/src/main/java/org/killbill/billing/usage/api/user/DefaultRolledUpUnit.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2014 Groupon, Inc
+ * Copyright 2014 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.usage.api.user;
+
+import org.killbill.billing.usage.api.RolledUpUnit;
+
+public class DefaultRolledUpUnit implements RolledUpUnit {
+
+    private final String unitType;
+    private final Long amount;
+
+    public DefaultRolledUpUnit(final String unitType, final Long amount) {
+        this.unitType = unitType;
+        this.amount = amount;
+    }
+
+    @Override
+    public String getUnitType() {
+        return unitType;
+    }
+
+    @Override
+    public Long getAmount() {
+        return amount;
+    }
+}
diff --git a/usage/src/main/java/org/killbill/billing/usage/api/user/DefaultRolledUpUsage.java b/usage/src/main/java/org/killbill/billing/usage/api/user/DefaultRolledUpUsage.java
index 3681acf..d28244c 100644
--- a/usage/src/main/java/org/killbill/billing/usage/api/user/DefaultRolledUpUsage.java
+++ b/usage/src/main/java/org/killbill/billing/usage/api/user/DefaultRolledUpUsage.java
@@ -16,34 +16,25 @@
 
 package org.killbill.billing.usage.api.user;
 
-import java.math.BigDecimal;
+import java.util.List;
 import java.util.UUID;
 
-import org.joda.time.DateTime;
-
+import org.joda.time.LocalDate;
+import org.killbill.billing.usage.api.RolledUpUnit;
 import org.killbill.billing.usage.api.RolledUpUsage;
-import org.killbill.billing.usage.dao.RolledUpUsageModelDao;
 
 public class DefaultRolledUpUsage implements RolledUpUsage {
 
     private final UUID subscriptionId;
-    private final String unitType;
-    private final DateTime startTime;
-    private final DateTime endTime;
-    private final BigDecimal amount;
+    private final LocalDate startDate;
+    private final LocalDate endDate;
+    private final List<RolledUpUnit> rolledUpUnits;
 
-    public DefaultRolledUpUsage(final UUID subscriptionId, final String unitType, final DateTime startTime, final DateTime endTime,
-                                final BigDecimal amount) {
+    public DefaultRolledUpUsage(final UUID subscriptionId, final LocalDate startDate, final LocalDate endDate, final List<RolledUpUnit> rolledUpUnits) {
         this.subscriptionId = subscriptionId;
-        this.unitType = unitType;
-        this.startTime = startTime;
-        this.endTime = endTime;
-        this.amount = amount;
-    }
-
-    public DefaultRolledUpUsage(final RolledUpUsageModelDao usageForSubscription) {
-        this(usageForSubscription.getSubscriptionId(), usageForSubscription.getUnitType(), usageForSubscription.getStartTime(),
-             usageForSubscription.getEndTime(), usageForSubscription.getAmount());
+        this.startDate = startDate;
+        this.endDate = endDate;
+        this.rolledUpUnits = rolledUpUnits;
     }
 
     @Override
@@ -51,75 +42,18 @@ public class DefaultRolledUpUsage implements RolledUpUsage {
         return subscriptionId;
     }
 
-    public String getUnitType() {
-        return unitType;
-    }
-
     @Override
-    public DateTime getStartTime() {
-        return startTime;
+    public LocalDate getStart() {
+        return startDate;
     }
 
     @Override
-    public DateTime getEndTime() {
-        return endTime;
-    }
-
-    @Override
-    public BigDecimal getAmount() {
-        return amount;
-    }
-
-    @Override
-    public String toString() {
-        final StringBuilder sb = new StringBuilder();
-        sb.append("DefaultRolledUpUsage");
-        sb.append("{subscriptionId=").append(subscriptionId);
-        sb.append(", unitType='").append(unitType).append('\'');
-        sb.append(", startTime=").append(startTime);
-        sb.append(", endTime=").append(endTime);
-        sb.append(", amount=").append(amount);
-        sb.append('}');
-        return sb.toString();
-    }
-
-    @Override
-    public boolean equals(final Object o) {
-        if (this == o) {
-            return true;
-        }
-        if (o == null || getClass() != o.getClass()) {
-            return false;
-        }
-
-        final DefaultRolledUpUsage that = (DefaultRolledUpUsage) o;
-
-        if (amount != null ? !amount.equals(that.amount) : that.amount != null) {
-            return false;
-        }
-        if (endTime != null ? !endTime.equals(that.endTime) : that.endTime != null) {
-            return false;
-        }
-        if (unitType != null ? !unitType.equals(that.unitType) : that.unitType != null) {
-            return false;
-        }
-        if (startTime != null ? !startTime.equals(that.startTime) : that.startTime != null) {
-            return false;
-        }
-        if (subscriptionId != null ? !subscriptionId.equals(that.subscriptionId) : that.subscriptionId != null) {
-            return false;
-        }
-
-        return true;
+    public LocalDate getEnd() {
+        return endDate;
     }
 
     @Override
-    public int hashCode() {
-        int result = subscriptionId != null ? subscriptionId.hashCode() : 0;
-        result = 31 * result + (unitType != null ? unitType.hashCode() : 0);
-        result = 31 * result + (startTime != null ? startTime.hashCode() : 0);
-        result = 31 * result + (endTime != null ? endTime.hashCode() : 0);
-        result = 31 * result + (amount != null ? amount.hashCode() : 0);
-        return result;
+    public List<RolledUpUnit> getRolledUpUnit() {
+        return rolledUpUnits;
     }
 }
diff --git a/usage/src/main/java/org/killbill/billing/usage/api/user/DefaultUsageUserApi.java b/usage/src/main/java/org/killbill/billing/usage/api/user/DefaultUsageUserApi.java
index 5b771d9..e1020f5 100644
--- a/usage/src/main/java/org/killbill/billing/usage/api/user/DefaultUsageUserApi.java
+++ b/usage/src/main/java/org/killbill/billing/usage/api/user/DefaultUsageUserApi.java
@@ -16,24 +16,27 @@
 
 package org.killbill.billing.usage.api.user;
 
-import java.math.BigDecimal;
 import java.util.ArrayList;
+import java.util.HashMap;
 import java.util.List;
-import java.util.Set;
+import java.util.Map;
 import java.util.UUID;
 
 import javax.inject.Inject;
 
-import org.joda.time.DateTime;
-
+import org.joda.time.LocalDate;
 import org.killbill.billing.ObjectType;
+import org.killbill.billing.callcontext.InternalCallContext;
 import org.killbill.billing.callcontext.InternalTenantContext;
+import org.killbill.billing.usage.api.RolledUpUnit;
 import org.killbill.billing.usage.api.RolledUpUsage;
+import org.killbill.billing.usage.api.SubscriptionUsageRecord;
+import org.killbill.billing.usage.api.UnitUsageRecord;
+import org.killbill.billing.usage.api.UsageRecord;
 import org.killbill.billing.usage.api.UsageUserApi;
 import org.killbill.billing.usage.dao.RolledUpUsageDao;
 import org.killbill.billing.usage.dao.RolledUpUsageModelDao;
 import org.killbill.billing.util.callcontext.CallContext;
-import org.killbill.billing.callcontext.InternalCallContext;
 import org.killbill.billing.util.callcontext.InternalCallContextFactory;
 import org.killbill.billing.util.callcontext.TenantContext;
 
@@ -50,33 +53,49 @@ public class DefaultUsageUserApi implements UsageUserApi {
     }
 
     @Override
-    public void recordRolledUpUsage(final UUID subscriptionId, final String unitType, final DateTime startTime, final DateTime endTime,
-                                    final BigDecimal amount, final CallContext context) {
-        final InternalCallContext internalCallContext = internalCallContextFactory.createInternalCallContext(subscriptionId, ObjectType.SUBSCRIPTION, context);
-        rolledUpUsageDao.record(subscriptionId, unitType, startTime, endTime, amount, internalCallContext);
+    public void recordRolledUpUsage(final SubscriptionUsageRecord record, final CallContext callContext) {
+        final InternalCallContext internalCallContext = internalCallContextFactory.createInternalCallContext(record.getSubscriptionId(), ObjectType.SUBSCRIPTION, callContext);
+        for (UnitUsageRecord unitUsageRecord : record.getPerUnitUsage()) {
+            for (UsageRecord usageRecord : unitUsageRecord.getDailyAmount()) {
+                rolledUpUsageDao.record(record.getSubscriptionId(), unitUsageRecord.getUnitType(), usageRecord.getDate(), usageRecord.getAmount(), internalCallContext);
+            }
+        }
     }
 
     @Override
-    public RolledUpUsage getUsageForSubscription(final UUID subscriptionId,  final String unitType, final DateTime startTime, final DateTime endTime, final TenantContext context) {
-        final RolledUpUsageModelDao usageForSubscription = rolledUpUsageDao.getUsageForSubscription(subscriptionId, startTime, endTime, unitType, internalCallContextFactory.createInternalTenantContext(context));
-        return new DefaultRolledUpUsage(usageForSubscription);
+    public RolledUpUsage getUsageForSubscription(final UUID subscriptionId, final String unitType, final LocalDate startDate, final LocalDate endDate, final TenantContext tenantContext) {
+        final List<RolledUpUsageModelDao> usageForSubscription = rolledUpUsageDao.getUsageForSubscription(subscriptionId, startDate, endDate, unitType, internalCallContextFactory.createInternalTenantContext(tenantContext));
+        final List<RolledUpUnit> rolledUpAmount = getRolledUpUnits(usageForSubscription);
+        return new DefaultRolledUpUsage(subscriptionId, startDate, endDate, rolledUpAmount);
     }
 
     @Override
-    public List<RolledUpUsage> getAllUsageForSubscription(final UUID subscriptionId, final Set<String> unitTypes, final List<DateTime> transitionTimes, final TenantContext tenantContext) {
-
+    public List<RolledUpUsage> getAllUsageForSubscription(final UUID subscriptionId, final List<LocalDate> transitionTimes, final TenantContext tenantContext) {
         final InternalTenantContext internalCallContext = internalCallContextFactory.createInternalTenantContext(subscriptionId, ObjectType.SUBSCRIPTION, tenantContext);
         List<RolledUpUsage> result = new ArrayList<RolledUpUsage>();
-        DateTime prevDate = null;
-        for (DateTime curDate : transitionTimes) {
+        LocalDate prevDate = null;
+        for (LocalDate curDate : transitionTimes) {
             if (prevDate != null) {
-                for (String unitType : unitTypes) {
-                    final RolledUpUsageModelDao usageForSubscription = rolledUpUsageDao.getUsageForSubscription(subscriptionId, prevDate, curDate, unitType, internalCallContext);
-                    result.add(new DefaultRolledUpUsage(usageForSubscription));
-                }
+                final List<RolledUpUsageModelDao> usageForSubscription = rolledUpUsageDao.getAllUsageForSubscription(subscriptionId, prevDate, curDate, internalCallContext);
+                final List<RolledUpUnit> rolledUpAmount = getRolledUpUnits(usageForSubscription);
+                result.add(new DefaultRolledUpUsage(subscriptionId, prevDate, curDate, rolledUpAmount));
             }
             prevDate = curDate;
         }
         return result;
     }
+
+    private List<RolledUpUnit> getRolledUpUnits(final List<RolledUpUsageModelDao> usageForSubscription) {
+        final Map<String, Long> tmp = new HashMap<String, Long>();
+        for (RolledUpUsageModelDao cur : usageForSubscription) {
+            Long currentAmount = tmp.get(cur.getUnitType());
+            Long updatedAmount = (currentAmount != null) ? currentAmount + cur.getAmount() : cur.getAmount();
+            tmp.put(cur.getUnitType(), updatedAmount);
+        }
+        final List<RolledUpUnit> result = new ArrayList<RolledUpUnit>(tmp.size());
+        for (final String unitType : tmp.keySet()) {
+            result.add(new DefaultRolledUpUnit(unitType, tmp.get(unitType)));
+        }
+        return result;
+    }
 }
diff --git a/usage/src/main/java/org/killbill/billing/usage/dao/DefaultRolledUpUsageDao.java b/usage/src/main/java/org/killbill/billing/usage/dao/DefaultRolledUpUsageDao.java
index 9c190c8..4923592 100644
--- a/usage/src/main/java/org/killbill/billing/usage/dao/DefaultRolledUpUsageDao.java
+++ b/usage/src/main/java/org/killbill/billing/usage/dao/DefaultRolledUpUsageDao.java
@@ -18,18 +18,14 @@ package org.killbill.billing.usage.dao;
 
 import java.math.BigDecimal;
 import java.util.List;
-import java.util.Set;
 import java.util.UUID;
 
 import javax.inject.Inject;
 
-import org.joda.time.DateTime;
-import org.killbill.billing.usage.api.RolledUpUsage;
-import org.killbill.billing.util.callcontext.TenantContext;
-import org.skife.jdbi.v2.IDBI;
-
+import org.joda.time.LocalDate;
 import org.killbill.billing.callcontext.InternalCallContext;
 import org.killbill.billing.callcontext.InternalTenantContext;
+import org.skife.jdbi.v2.IDBI;
 
 public class DefaultRolledUpUsageDao implements RolledUpUsageDao {
 
@@ -41,17 +37,18 @@ public class DefaultRolledUpUsageDao implements RolledUpUsageDao {
     }
 
     @Override
-    public void record(final UUID subscriptionId, final String unitType, final DateTime startTime, final DateTime endTime,
-                       final BigDecimal amount, final InternalCallContext context) {
-        final RolledUpUsageModelDao rolledUpUsageModelDao = new RolledUpUsageModelDao(subscriptionId, unitType, startTime,
-                                                                                      endTime, amount
-        );
+    public void record(final UUID subscriptionId, final String unitType, final LocalDate date, final Long amount, final InternalCallContext context) {
+        final RolledUpUsageModelDao rolledUpUsageModelDao = new RolledUpUsageModelDao(subscriptionId, unitType, date, amount);
         rolledUpUsageSqlDao.create(rolledUpUsageModelDao, context);
     }
 
     @Override
-    public RolledUpUsageModelDao getUsageForSubscription(UUID subscriptionId, DateTime startTime, DateTime endTime, String unitType, InternalTenantContext context) {
-        final BigDecimal amount = rolledUpUsageSqlDao.getUsageForSubscription(subscriptionId, startTime.toDate(), endTime.toDate(), unitType, context);
-        return new RolledUpUsageModelDao(subscriptionId, unitType, startTime, endTime, amount != null ? amount : BigDecimal.ZERO);
+    public List<RolledUpUsageModelDao> getUsageForSubscription(final UUID subscriptionId, final LocalDate startDate, final LocalDate endDate, final String unitType, final InternalTenantContext context) {
+        return rolledUpUsageSqlDao.getUsageForSubscription(subscriptionId, startDate.toDate(), endDate.toDate(), unitType, context);
+    }
+
+    @Override
+    public List<RolledUpUsageModelDao> getAllUsageForSubscription(final UUID subscriptionId, final LocalDate startDate, final LocalDate endDate, final InternalTenantContext context) {
+        return rolledUpUsageSqlDao.getAllUsageForSubscription(subscriptionId, startDate.toDate(), endDate.toDate(), context);
     }
 }
diff --git a/usage/src/main/java/org/killbill/billing/usage/dao/RolledUpUsageDao.java b/usage/src/main/java/org/killbill/billing/usage/dao/RolledUpUsageDao.java
index 225b3c8..4144aed 100644
--- a/usage/src/main/java/org/killbill/billing/usage/dao/RolledUpUsageDao.java
+++ b/usage/src/main/java/org/killbill/billing/usage/dao/RolledUpUsageDao.java
@@ -16,22 +16,19 @@
 
 package org.killbill.billing.usage.dao;
 
-import java.math.BigDecimal;
 import java.util.List;
-import java.util.Set;
 import java.util.UUID;
 
-import org.joda.time.DateTime;
-
+import org.joda.time.LocalDate;
 import org.killbill.billing.callcontext.InternalCallContext;
 import org.killbill.billing.callcontext.InternalTenantContext;
-import org.killbill.billing.usage.api.RolledUpUsage;
-import org.killbill.billing.util.callcontext.TenantContext;
 
 public interface RolledUpUsageDao {
 
-    void record(UUID subscriptionId, String unitType, DateTime startTime,
-                DateTime endTime, BigDecimal amount, InternalCallContext context);
+    void record(UUID subscriptionId, String unitType, LocalDate date,
+                Long amount, InternalCallContext context);
+
+    List<RolledUpUsageModelDao> getUsageForSubscription(UUID subscriptionId, LocalDate startDate, LocalDate endDate, String unitType, InternalTenantContext context);
 
-    RolledUpUsageModelDao getUsageForSubscription(UUID subscriptionId, DateTime startTime, DateTime endTime, String unitType, InternalTenantContext context);
+    List<RolledUpUsageModelDao> getAllUsageForSubscription(UUID subscriptionId, LocalDate startDate, LocalDate endDate, InternalTenantContext context);
 }
diff --git a/usage/src/main/java/org/killbill/billing/usage/dao/RolledUpUsageModelDao.java b/usage/src/main/java/org/killbill/billing/usage/dao/RolledUpUsageModelDao.java
index acffd5b..b9a5a62 100644
--- a/usage/src/main/java/org/killbill/billing/usage/dao/RolledUpUsageModelDao.java
+++ b/usage/src/main/java/org/killbill/billing/usage/dao/RolledUpUsageModelDao.java
@@ -16,34 +16,34 @@
 
 package org.killbill.billing.usage.dao;
 
-import java.math.BigDecimal;
 import java.util.UUID;
 
 import org.joda.time.DateTime;
+import org.joda.time.LocalDate;
+import org.killbill.billing.util.dao.TableName;
+import org.killbill.billing.util.entity.Entity;
+import org.killbill.billing.util.entity.dao.EntityModelDao;
+import org.killbill.billing.util.entity.dao.EntityModelDaoBase;
 
-public class RolledUpUsageModelDao {
+public class RolledUpUsageModelDao extends EntityModelDaoBase implements EntityModelDao<Entity> {
 
-    private UUID id;
     private UUID subscriptionId;
     private String unitType;
-    private DateTime startTime;
-    private DateTime endTime;
-    private BigDecimal amount;
+    private LocalDate recordDate;
+    private Long amount;
 
     public RolledUpUsageModelDao() { /* For the DAO mapper */ }
 
-    public RolledUpUsageModelDao(final UUID subscriptionId, final String unitType, final DateTime startTime,
-                                 final DateTime endTime, final BigDecimal amount) {
-        this.id = UUID.randomUUID();
+    public RolledUpUsageModelDao(final UUID id, final DateTime createdDate, final DateTime updatedDate, final UUID subscriptionId, final String unitType, final LocalDate recordDate, final Long amount) {
+        super(id, createdDate, updatedDate);
         this.subscriptionId = subscriptionId;
         this.unitType = unitType;
-        this.startTime = startTime;
-        this.endTime = endTime;
+        this.recordDate = recordDate;
         this.amount = amount;
     }
 
-    public UUID getId() {
-        return id;
+    public RolledUpUsageModelDao(final UUID subscriptionId, final String unitType, final LocalDate recordDate, final Long amount) {
+        this(UUID.randomUUID(), null, null, subscriptionId, unitType, recordDate, amount);
     }
 
     public UUID getSubscriptionId() {
@@ -54,22 +54,18 @@ public class RolledUpUsageModelDao {
         return unitType;
     }
 
-    public DateTime getStartTime() {
-        return startTime;
+    public LocalDate getRecordDate() {
+        return recordDate;
     }
 
-    public DateTime getEndTime() {
-        return endTime;
+    public void setRecordDate(final LocalDate recordDate) {
+        this.recordDate = recordDate;
     }
 
-    public BigDecimal getAmount() {
+    public Long getAmount() {
         return amount;
     }
 
-    public void setId(final UUID id) {
-        this.id = id;
-    }
-
     public void setSubscriptionId(final UUID subscriptionId) {
         this.subscriptionId = subscriptionId;
     }
@@ -78,15 +74,7 @@ public class RolledUpUsageModelDao {
         this.unitType = unitType;
     }
 
-    public void setStartTime(final DateTime startTime) {
-        this.startTime = startTime;
-    }
-
-    public void setEndTime(final DateTime endTime) {
-        this.endTime = endTime;
-    }
-
-    public void setAmount(final BigDecimal amount) {
+    public void setAmount(final Long amount) {
         this.amount = amount;
     }
 
@@ -97,8 +85,7 @@ public class RolledUpUsageModelDao {
         sb.append("{id=").append(id);
         sb.append(", subscriptionId=").append(subscriptionId);
         sb.append(", unitType='").append(unitType).append('\'');
-        sb.append(", startTime=").append(startTime);
-        sb.append(", endTime=").append(endTime);
+        sb.append(", recordDate=").append(recordDate);
         sb.append(", amount=").append(amount);
         sb.append('}');
         return sb.toString();
@@ -118,7 +105,7 @@ public class RolledUpUsageModelDao {
         if (amount != null ? !amount.equals(that.amount) : that.amount != null) {
             return false;
         }
-        if (endTime != null ? !endTime.equals(that.endTime) : that.endTime != null) {
+        if (recordDate != null ? !recordDate.equals(that.recordDate) : that.recordDate != null) {
             return false;
         }
         if (id != null ? !id.equals(that.id) : that.id != null) {
@@ -127,9 +114,6 @@ public class RolledUpUsageModelDao {
         if (unitType != null ? !unitType.equals(that.unitType) : that.unitType != null) {
             return false;
         }
-        if (startTime != null ? !startTime.equals(that.startTime) : that.startTime != null) {
-            return false;
-        }
         if (subscriptionId != null ? !subscriptionId.equals(that.subscriptionId) : that.subscriptionId != null) {
             return false;
         }
@@ -142,9 +126,18 @@ public class RolledUpUsageModelDao {
         int result = id != null ? id.hashCode() : 0;
         result = 31 * result + (subscriptionId != null ? subscriptionId.hashCode() : 0);
         result = 31 * result + (unitType != null ? unitType.hashCode() : 0);
-        result = 31 * result + (startTime != null ? startTime.hashCode() : 0);
-        result = 31 * result + (endTime != null ? endTime.hashCode() : 0);
+        result = 31 * result + (recordDate != null ? recordDate.hashCode() : 0);
         result = 31 * result + (amount != null ? amount.hashCode() : 0);
         return result;
     }
+
+    @Override
+    public TableName getTableName() {
+        return TableName.ROLLED_UP_USAGE;
+    }
+
+    @Override
+    public TableName getHistoryTableName() {
+        return null;
+    }
 }
diff --git a/usage/src/main/java/org/killbill/billing/usage/dao/RolledUpUsageSqlDao.java b/usage/src/main/java/org/killbill/billing/usage/dao/RolledUpUsageSqlDao.java
index 4bfdb8a..b237eb2 100644
--- a/usage/src/main/java/org/killbill/billing/usage/dao/RolledUpUsageSqlDao.java
+++ b/usage/src/main/java/org/killbill/billing/usage/dao/RolledUpUsageSqlDao.java
@@ -16,31 +16,38 @@
 
 package org.killbill.billing.usage.dao;
 
-import java.math.BigDecimal;
 import java.util.Date;
+import java.util.List;
 import java.util.UUID;
 
+import org.killbill.billing.callcontext.InternalCallContext;
+import org.killbill.billing.callcontext.InternalTenantContext;
+import org.killbill.billing.util.callcontext.InternalTenantContextBinder;
+import org.killbill.billing.util.entity.Entity;
+import org.killbill.billing.util.entity.dao.EntitySqlDao;
+import org.killbill.billing.util.entity.dao.EntitySqlDaoStringTemplate;
 import org.skife.jdbi.v2.sqlobject.Bind;
 import org.skife.jdbi.v2.sqlobject.BindBean;
 import org.skife.jdbi.v2.sqlobject.SqlQuery;
 import org.skife.jdbi.v2.sqlobject.SqlUpdate;
-import org.skife.jdbi.v2.sqlobject.stringtemplate.UseStringTemplate3StatementLocator;
 
-import org.killbill.billing.callcontext.InternalCallContext;
-import org.killbill.billing.callcontext.InternalTenantContext;
-import org.killbill.billing.util.callcontext.InternalTenantContextBinder;
-
-@UseStringTemplate3StatementLocator()
-public interface RolledUpUsageSqlDao {
+@EntitySqlDaoStringTemplate()
+public interface RolledUpUsageSqlDao extends EntitySqlDao<RolledUpUsageModelDao, Entity> {
 
     @SqlUpdate
     public void create(@BindBean RolledUpUsageModelDao rolledUpUsage,
                        @InternalTenantContextBinder final InternalCallContext context);
 
     @SqlQuery
-    public BigDecimal getUsageForSubscription(@Bind("subscriptionId") final UUID subscriptionId,
-                                                         @Bind("startTime") final Date startTime,
-                                                         @Bind("endTime") final Date endTime,
-                                                         @Bind("unitType") final String unitType,
-                                                         @InternalTenantContextBinder final InternalTenantContext context);
+    public List<RolledUpUsageModelDao> getUsageForSubscription(@Bind("subscriptionId") final UUID subscriptionId,
+                                                               @Bind("startDate") final Date startDate,
+                                                               @Bind("endDate") final Date endDate,
+                                                               @Bind("unitType") final String unitType,
+                                                               @InternalTenantContextBinder final InternalTenantContext context);
+
+    @SqlQuery
+    public List<RolledUpUsageModelDao> getAllUsageForSubscription(@Bind("subscriptionId") final UUID subscriptionId,
+                                                                  @Bind("startDate") final Date startDate,
+                                                                  @Bind("endDate") final Date endDate,
+                                                                  @InternalTenantContextBinder final InternalTenantContext context);
 }
diff --git a/usage/src/main/resources/org/killbill/billing/usage/dao/RolledUpUsageSqlDao.sql.stg b/usage/src/main/resources/org/killbill/billing/usage/dao/RolledUpUsageSqlDao.sql.stg
index 431d976..ea40a86 100644
--- a/usage/src/main/resources/org/killbill/billing/usage/dao/RolledUpUsageSqlDao.sql.stg
+++ b/usage/src/main/resources/org/killbill/billing/usage/dao/RolledUpUsageSqlDao.sql.stg
@@ -1,54 +1,46 @@
-group RolledUpUsageSqlDao: EntitySqlDao;
+group RolledUpUsageSqlDao : EntitySqlDao;
 
 tableName() ::= "rolled_up_usage"
 
+
 tableFields(prefix) ::= <<
-  <prefix>id
-, <prefix>subscription_id
+  <prefix>subscription_id
 , <prefix>unit_type
-, <prefix>start_time
-, <prefix>end_time
+, <prefix>record_date
 , <prefix>amount
 , <prefix>created_by
 , <prefix>created_date
-, <prefix>account_record_id
-, <prefix>tenant_record_id
 >>
 
 tableValues() ::= <<
-  :id
-, :subscriptionId
+  :subscriptionId
 , :unitType
-, :startTime
-, :endTime
+, :recordDate
 , :amount
 , :userName
 , :createdDate
-, :accountRecordId
-, :tenantRecordId
 >>
 
-CHECK_TENANT(prefix) ::= "<prefix>tenant_record_id = :tenantRecordId"
-AND_CHECK_TENANT(prefix) ::= "and <CHECK_TENANT(prefix)>"
 
-create() ::= <<
-insert into <tableName()> (
-  <tableFields()>
-)
-values (
-  <tableValues()>
-)
+getUsageForSubscription() ::= <<
+select
+  <allTableFields()>
+from <tableName()>
+where subscription_id = :subscriptionId
+and record_date >= :startDate
+and record_date \< :endDate
+and unit_type = :unitType
+<AND_CHECK_TENANT()>
 ;
 >>
 
-getUsageForSubscription() ::= <<
+getAllUsageForSubscription() ::= <<
 select
-  sum(amount)
-from <tableName()> t
+  <allTableFields()>
+from <tableName()>
 where subscription_id = :subscriptionId
-and start_time >= :startTime
-and end_time \<= :endTime
-and unit_type = :unitType
+and record_date >= :startDate
+and record_date \< :endDate
 <AND_CHECK_TENANT()>
 ;
 >>
diff --git a/usage/src/main/resources/org/killbill/billing/usage/ddl.sql b/usage/src/main/resources/org/killbill/billing/usage/ddl.sql
index 2d8edc9..50b3f2d 100644
--- a/usage/src/main/resources/org/killbill/billing/usage/ddl.sql
+++ b/usage/src/main/resources/org/killbill/billing/usage/ddl.sql
@@ -6,9 +6,8 @@ CREATE TABLE rolled_up_usage (
     id char(36) NOT NULL,
     subscription_id char(36),
     unit_type varchar(50),
-    start_time datetime NOT NULL,
-    end_time datetime,
-    amount decimal(15,9) NOT NULL,
+    record_date date NOT NULL,
+    amount bigint NOT NULL,
     created_by varchar(50) NOT NULL,
     created_date datetime NOT NULL,
     account_record_id int(11) unsigned default null,
diff --git a/usage/src/test/java/org/killbill/billing/usage/api/user/MockUsageUserApi.java b/usage/src/test/java/org/killbill/billing/usage/api/user/MockUsageUserApi.java
index d3dd47a..4ec7e95 100644
--- a/usage/src/test/java/org/killbill/billing/usage/api/user/MockUsageUserApi.java
+++ b/usage/src/test/java/org/killbill/billing/usage/api/user/MockUsageUserApi.java
@@ -16,38 +16,38 @@
 
 package org.killbill.billing.usage.api.user;
 
-import java.math.BigDecimal;
 import java.util.List;
 import java.util.Set;
 import java.util.UUID;
 
-import org.joda.time.DateTime;
+import org.joda.time.LocalDate;
 import org.killbill.billing.usage.api.RolledUpUsage;
+import org.killbill.billing.usage.api.SubscriptionUsageRecord;
 import org.killbill.billing.usage.api.UsageUserApi;
 import org.killbill.billing.util.callcontext.CallContext;
 import org.killbill.billing.util.callcontext.TenantContext;
 
-
 public class MockUsageUserApi implements UsageUserApi {
 
     private List<RolledUpUsage> result;
 
-    @Override
-    public void recordRolledUpUsage(final UUID uuid, final String s, final DateTime dateTime, final DateTime dateTime2, final BigDecimal bigDecimal, final CallContext callContext) {
-        throw new UnsupportedOperationException();
+    public void setAllUsageForSubscription(final List<RolledUpUsage> result) {
+        this.result = result;
     }
 
     @Override
-    public RolledUpUsage getUsageForSubscription(final UUID uuid, final String s, final DateTime dateTime, final DateTime dateTime2, final TenantContext tenantContext) {
-        throw new UnsupportedOperationException();
+    public void recordRolledUpUsage(final SubscriptionUsageRecord subscriptionUsageRecord, final CallContext callContext) {
+
     }
 
     @Override
-    public List<RolledUpUsage> getAllUsageForSubscription(final UUID uuid, final Set<String> strings, final List<DateTime> dateTimes, final TenantContext tenantContext) {
-        return result;
+    public RolledUpUsage getUsageForSubscription(final UUID uuid, final String s, final LocalDate localDate, final LocalDate localDate2, final TenantContext tenantContext) {
+        return null;
     }
 
-    public void setAllUsageForSubscription(final List<RolledUpUsage> result) {
-        this.result = result;
+    @Override
+    public List<RolledUpUsage> getAllUsageForSubscription(final UUID uuid, final List<LocalDate> localDates, final TenantContext tenantContext) {
+        return null;
     }
+
 }
diff --git a/usage/src/test/java/org/killbill/billing/usage/dao/TestDefaultRolledUpUsageDao.java b/usage/src/test/java/org/killbill/billing/usage/dao/TestDefaultRolledUpUsageDao.java
index fad80b4..7e9bca4 100644
--- a/usage/src/test/java/org/killbill/billing/usage/dao/TestDefaultRolledUpUsageDao.java
+++ b/usage/src/test/java/org/killbill/billing/usage/dao/TestDefaultRolledUpUsageDao.java
@@ -16,11 +16,10 @@
 
 package org.killbill.billing.usage.dao;
 
-import java.math.BigDecimal;
+import java.util.List;
 import java.util.UUID;
 
-import org.joda.time.DateTime;
-import org.joda.time.DateTimeZone;
+import org.joda.time.LocalDate;
 import org.killbill.billing.usage.UsageTestSuiteWithEmbeddedDB;
 import org.testng.annotations.BeforeMethod;
 import org.testng.annotations.Test;
@@ -37,39 +36,72 @@ public class TestDefaultRolledUpUsageDao extends UsageTestSuiteWithEmbeddedDB {
     public void testSimple() {
         final UUID subscriptionId = UUID.randomUUID();
         final String unitType = "foo";
-        final DateTime startDate = new DateTime(2013, 1, 1, 0, 0, DateTimeZone.UTC);
-        final DateTime endDate = new DateTime(2013, 2, 1, 0, 0, DateTimeZone.UTC);
-        final BigDecimal amount1 = BigDecimal.TEN;
-        final BigDecimal amount2 = BigDecimal.TEN;
-
-        rolledUpUsageDao.record(subscriptionId, unitType, startDate, endDate, amount1, internalCallContext);
-        rolledUpUsageDao.record(subscriptionId, unitType, startDate, endDate, amount2, internalCallContext);
-
-        final RolledUpUsageModelDao result = rolledUpUsageDao.getUsageForSubscription(subscriptionId, startDate, endDate, unitType, internalCallContext);
-        assertEquals(result.getSubscriptionId(), subscriptionId);
-        assertEquals(result.getStartTime().compareTo(startDate), 0);
-        assertEquals(result.getEndTime().compareTo(endDate), 0);
-        assertEquals(result.getUnitType(), unitType);
-        assertEquals(result.getSubscriptionId(), subscriptionId);
-        assertEquals(result.getSubscriptionId(), subscriptionId);
-        assertEquals(result.getAmount().compareTo(amount1.add(amount2)), 0);
+        final LocalDate startDate = new LocalDate(2013, 1, 1);
+        final LocalDate endDate = new LocalDate(2013, 2, 1);
+        final Long amount1 = 10L;
+        final Long amount2 = 5L;
+
+        rolledUpUsageDao.record(subscriptionId, unitType, startDate, amount1, internalCallContext);
+        rolledUpUsageDao.record(subscriptionId, unitType, endDate.minusDays(1), amount2, internalCallContext);
+
+        final List<RolledUpUsageModelDao> result = rolledUpUsageDao.getUsageForSubscription(subscriptionId, startDate, endDate, unitType, internalCallContext);
+        assertEquals(result.size(), 2);
+        assertEquals(result.get(0).getSubscriptionId(), subscriptionId);
+        assertEquals(result.get(0).getRecordDate().compareTo(startDate), 0);
+        assertEquals(result.get(0).getUnitType(), unitType);
+        assertEquals(result.get(0).getAmount().compareTo(amount1), 0);
+        assertEquals(result.get(1).getSubscriptionId(), subscriptionId);
+        assertEquals(result.get(1).getRecordDate().compareTo(endDate.minusDays(1)), 0);
+        assertEquals(result.get(1).getUnitType(), unitType);
+        assertEquals(result.get(1).getAmount().compareTo(amount2), 0);
+    }
+
+
+    @Test(groups = "slow")
+    public void testMultipleUnits() {
+        final UUID subscriptionId = UUID.randomUUID();
+        final String unitType1 = "foo";
+        final String unitType2 = "bar";
+        final LocalDate startDate = new LocalDate(2013, 1, 1);
+        final LocalDate endDate = new LocalDate(2013, 2, 1);
+        final Long amount1 = 10L;
+        final Long amount2 = 5L;
+        final Long amount3 = 13L;
+
+        rolledUpUsageDao.record(subscriptionId, unitType1, startDate, amount1, internalCallContext);
+        rolledUpUsageDao.record(subscriptionId, unitType1, startDate.plusDays(1), amount2, internalCallContext);
+
+        rolledUpUsageDao.record(subscriptionId, unitType2, endDate.minusDays(1), amount3, internalCallContext);
+
+        final List<RolledUpUsageModelDao> result = rolledUpUsageDao.getAllUsageForSubscription(subscriptionId, startDate, endDate, internalCallContext);
+        assertEquals(result.size(), 3);
+        assertEquals(result.get(0).getSubscriptionId(), subscriptionId);
+        assertEquals(result.get(0).getRecordDate().compareTo(startDate), 0);
+        assertEquals(result.get(0).getUnitType(), unitType1);
+        assertEquals(result.get(0).getAmount().compareTo(amount1), 0);
+        assertEquals(result.get(1).getSubscriptionId(), subscriptionId);
+        assertEquals(result.get(1).getRecordDate().compareTo(startDate.plusDays(1)), 0);
+        assertEquals(result.get(1).getUnitType(), unitType1);
+        assertEquals(result.get(1).getAmount().compareTo(amount2), 0);
+        assertEquals(result.get(2).getSubscriptionId(), subscriptionId);
+        assertEquals(result.get(2).getRecordDate().compareTo(endDate.minusDays(1)), 0);
+        assertEquals(result.get(2).getUnitType(), unitType2);
+        assertEquals(result.get(2).getAmount().compareTo(amount3), 0);
     }
 
+
+
     @Test(groups = "slow")
     public void testNoEntries() {
         final UUID subscriptionId = UUID.randomUUID();
         final String unitType = "foo";
-        final DateTime startDate = new DateTime(2013, 1, 1, 0, 0, DateTimeZone.UTC);
-        final DateTime endDate = new DateTime(2013, 2, 1, 0, 0, DateTimeZone.UTC);
-
-        final RolledUpUsageModelDao result = rolledUpUsageDao.getUsageForSubscription(subscriptionId, startDate, endDate, unitType, internalCallContext);
-        assertEquals(result.getSubscriptionId(), subscriptionId);
-        assertEquals(result.getStartTime().compareTo(startDate), 0);
-        assertEquals(result.getEndTime().compareTo(endDate), 0);
-        assertEquals(result.getUnitType(), unitType);
-        assertEquals(result.getSubscriptionId(), subscriptionId);
-        assertEquals(result.getSubscriptionId(), subscriptionId);
-        assertEquals(result.getAmount().compareTo(BigDecimal.ZERO), 0);
+        final LocalDate startDate = new LocalDate(2013, 1, 1);
+        final LocalDate endDate = new LocalDate(2013, 2, 1);
+
+        rolledUpUsageDao.record(subscriptionId, unitType, endDate, 9L, internalCallContext);
+
+        final List<RolledUpUsageModelDao> result = rolledUpUsageDao.getUsageForSubscription(subscriptionId, startDate, endDate, unitType, internalCallContext);
+        assertEquals(result.size(), 0);
     }
 
 }

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

diff --git a/util/pom.xml b/util/pom.xml
index 71477c8..183555f 100644
--- a/util/pom.xml
+++ b/util/pom.xml
@@ -12,7 +12,7 @@
     <parent>
         <artifactId>killbill</artifactId>
         <groupId>org.kill-bill.billing</groupId>
-        <version>0.11.10-SNAPSHOT</version>
+        <version>0.11.11-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>killbill-util</artifactId>
diff --git a/util/src/main/java/org/killbill/billing/util/config/InvoiceConfig.java b/util/src/main/java/org/killbill/billing/util/config/InvoiceConfig.java
index cd71a12..c32ec52 100644
--- a/util/src/main/java/org/killbill/billing/util/config/InvoiceConfig.java
+++ b/util/src/main/java/org/killbill/billing/util/config/InvoiceConfig.java
@@ -32,4 +32,9 @@ public interface InvoiceConfig extends KillbillConfig {
     @Description("Whether to send email notifications on invoice creation (for configured accounts)")
     public boolean isEmailNotificationsEnabled();
 
+    @Config("org.killbill.invoice.usage.insert.zero.amount")
+    @Default("true")
+    @Description("Whether to insert usage items with a zero amount")
+    public boolean isInsertZeroUsageItems();
+
 }
diff --git a/util/src/main/java/org/killbill/billing/util/dao/TableName.java b/util/src/main/java/org/killbill/billing/util/dao/TableName.java
index bb75a51..54edeb3 100644
--- a/util/src/main/java/org/killbill/billing/util/dao/TableName.java
+++ b/util/src/main/java/org/killbill/billing/util/dao/TableName.java
@@ -52,7 +52,8 @@ public enum TableName {
     TAG_HISTORY("tag_history"),
     TENANT("tenants", ObjectType.TENANT),
     TENANT_KVS("tenant_kvs", ObjectType.TENANT_KVS),
-    TAG("tags", ObjectType.TAG, TAG_HISTORY);
+    TAG("tags", ObjectType.TAG, TAG_HISTORY),
+    ROLLED_UP_USAGE("rolled_up_usage");
 
     private final String tableName;
     private final ObjectType objectType;
diff --git a/util/src/main/resources/org/killbill/billing/util/ddl.sql b/util/src/main/resources/org/killbill/billing/util/ddl.sql
index 2704dc9..db53c9c 100644
--- a/util/src/main/resources/org/killbill/billing/util/ddl.sql
+++ b/util/src/main/resources/org/killbill/billing/util/ddl.sql
@@ -174,7 +174,7 @@ CREATE INDEX notifications_tenant_account_record_id ON notifications(search_key2
 
 DROP TABLE IF EXISTS notifications_history;
 CREATE TABLE notifications_history (
-    record_id int(11) unsigned NOT NULL AUTO_INCREMENT,
+    record_id int(11) unsigned NOT NULL,
     class_name varchar(256) NOT NULL,
     event_json varchar(2048) NOT NULL,
     user_token char(36),
@@ -213,7 +213,7 @@ CREATE INDEX bus_events_tenant_account_record_id ON bus_events(search_key2, sear
 
 DROP TABLE IF EXISTS bus_events_history;
 CREATE TABLE bus_events_history (
-    record_id int(11) unsigned NOT NULL AUTO_INCREMENT,
+    record_id int(11) unsigned NOT NULL,
     class_name varchar(128) NOT NULL,
     event_json varchar(2048) NOT NULL,
     user_token char(36),