killbill-memoizeit

Changes

NEWS 5(+5 -0)

pom.xml 2(+1 -1)

Details

diff --git a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/overdue/TestOverdueBase.java b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/overdue/TestOverdueBase.java
index e76b309..613cd98 100644
--- a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/overdue/TestOverdueBase.java
+++ b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/overdue/TestOverdueBase.java
@@ -30,6 +30,7 @@ import org.killbill.billing.catalog.api.BillingPeriod;
 import org.killbill.billing.entitlement.api.BlockingStateType;
 import org.killbill.billing.entitlement.api.SubscriptionBundle;
 import org.killbill.billing.overdue.OverdueService;
+import org.killbill.billing.overdue.api.DefaultOverdueUserApi;
 import org.killbill.billing.overdue.config.OverdueConfig;
 import org.killbill.billing.payment.api.PaymentMethodPlugin;
 import org.killbill.billing.payment.api.TestPaymentMethodPluginBase;
@@ -60,6 +61,8 @@ public abstract class TestOverdueBase extends TestIntegrationBase {
         final InputStream is = new ByteArrayInputStream(configXml.getBytes());
         final OverdueConfig config = XMLLoader.getObjectFromStreamNoValidation(is, OverdueConfig.class);
         overdueWrapperFactory.setOverdueConfig(config);
+        overdueListener.setOverdueConfig(config);
+        ((DefaultOverdueUserApi) overdueUserApi).setOverdueConfig(config);
 
         account = createAccountWithNonOsgiPaymentMethod(getAccountData(0));
         assertNotNull(account);
diff --git a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestIntegrationBase.java b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestIntegrationBase.java
index 0e0029e..ada01fe 100644
--- a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestIntegrationBase.java
+++ b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestIntegrationBase.java
@@ -70,6 +70,7 @@ import org.killbill.billing.lifecycle.glue.BusModule;
 import org.killbill.billing.mock.MockAccountBuilder;
 import org.killbill.billing.osgi.config.OSGIConfig;
 import org.killbill.billing.overdue.OverdueUserApi;
+import org.killbill.billing.overdue.listener.OverdueListener;
 import org.killbill.billing.overdue.wrapper.OverdueWrapperFactory;
 import org.killbill.billing.payment.api.Payment;
 import org.killbill.billing.payment.api.PaymentApi;
@@ -196,6 +197,9 @@ public class TestIntegrationBase extends BeatrixTestSuiteWithEmbeddedDB {
     protected OverdueWrapperFactory overdueWrapperFactory;
 
     @Inject
+    protected OverdueListener overdueListener;
+
+    @Inject
     protected AccountUserApi accountUserApi;
 
     @Inject

NEWS 5(+5 -0)

diff --git a/NEWS b/NEWS
index 767f9ea..6dd213b 100644
--- a/NEWS
+++ b/NEWS
@@ -1,3 +1,8 @@
+0.11.10
+    Fix broken logging (war artifacts)
+    https://github.com/killbill/killbill/issues/210
+    Update killbill-oss-parent to 0.7.22
+
 0.11.9
     payment: rework Janitor shutdown sequence
     jdbc: integrate log4jdbc-log4j2
diff --git a/overdue/src/main/java/org/killbill/billing/overdue/config/DefaultOverdueStateSet.java b/overdue/src/main/java/org/killbill/billing/overdue/config/DefaultOverdueStateSet.java
index 615d0a6..abf0518 100644
--- a/overdue/src/main/java/org/killbill/billing/overdue/config/DefaultOverdueStateSet.java
+++ b/overdue/src/main/java/org/killbill/billing/overdue/config/DefaultOverdueStateSet.java
@@ -37,7 +37,7 @@ public abstract class DefaultOverdueStateSet extends ValidatingConfig<OverdueCon
     private static final Period ZERO_PERIOD = new Period();
     private final DefaultOverdueState clearState = new DefaultOverdueState().setName(DefaultBlockingState.CLEAR_STATE_NAME).setClearState(true);
 
-    protected abstract DefaultOverdueState[] getStates();
+    public abstract DefaultOverdueState[] getStates();
 
     @Override
     public OverdueState findState(final String stateName) throws OverdueApiException {
diff --git a/overdue/src/main/java/org/killbill/billing/overdue/config/OverdueStatesAccount.java b/overdue/src/main/java/org/killbill/billing/overdue/config/OverdueStatesAccount.java
index afca345..fb3db68 100644
--- a/overdue/src/main/java/org/killbill/billing/overdue/config/OverdueStatesAccount.java
+++ b/overdue/src/main/java/org/killbill/billing/overdue/config/OverdueStatesAccount.java
@@ -32,7 +32,7 @@ public class OverdueStatesAccount extends DefaultOverdueStateSet {
     private DefaultOverdueState[] accountOverdueStates = new DefaultOverdueState[0];
 
     @Override
-    protected DefaultOverdueState[] getStates() {
+    public DefaultOverdueState[] getStates() {
         return accountOverdueStates;
     }
 
diff --git a/overdue/src/main/java/org/killbill/billing/overdue/glue/DefaultOverdueModule.java b/overdue/src/main/java/org/killbill/billing/overdue/glue/DefaultOverdueModule.java
index f09c6c9..d3a5b46 100644
--- a/overdue/src/main/java/org/killbill/billing/overdue/glue/DefaultOverdueModule.java
+++ b/overdue/src/main/java/org/killbill/billing/overdue/glue/DefaultOverdueModule.java
@@ -26,6 +26,7 @@ import org.killbill.billing.overdue.api.DefaultOverdueUserApi;
 import org.killbill.billing.overdue.applicator.OverdueEmailGenerator;
 import org.killbill.billing.overdue.applicator.formatters.DefaultOverdueEmailFormatterFactory;
 import org.killbill.billing.overdue.applicator.formatters.OverdueEmailFormatterFactory;
+import org.killbill.billing.overdue.listener.OverdueListener;
 import org.killbill.billing.overdue.notification.OverdueAsyncBusNotifier;
 import org.killbill.billing.overdue.notification.OverdueAsyncBusPoster;
 import org.killbill.billing.overdue.notification.OverdueCheckNotifier;
@@ -61,6 +62,8 @@ public class DefaultOverdueModule extends KillBillModule implements OverdueModul
         final OverdueProperties config = new ConfigurationObjectFactory(skifeConfigSource).build(OverdueProperties.class);
         bind(OverdueProperties.class).toInstance(config);
 
+        bind(OverdueListener.class).asEagerSingleton();
+
         bind(OverdueNotifier.class).annotatedWith(Names.named(OVERDUE_NOTIFIER_CHECK_NAMED)).to(OverdueCheckNotifier.class).asEagerSingleton();
         bind(OverdueNotifier.class).annotatedWith(Names.named(OVERDUE_NOTIFIER_ASYNC_BUS_NAMED)).to(OverdueAsyncBusNotifier.class).asEagerSingleton();
 
diff --git a/overdue/src/main/java/org/killbill/billing/overdue/listener/OverdueListener.java b/overdue/src/main/java/org/killbill/billing/overdue/listener/OverdueListener.java
index dfb24f2..2bcd2b8 100644
--- a/overdue/src/main/java/org/killbill/billing/overdue/listener/OverdueListener.java
+++ b/overdue/src/main/java/org/killbill/billing/overdue/listener/OverdueListener.java
@@ -1,7 +1,9 @@
 /*
  * Copyright 2010-2013 Ning, Inc.
+ * Copyright 2014 Groupon, Inc
+ * Copyright 2014 The Billing Project, LLC
  *
- * Ning licenses this file to you under the Apache License, version 2.0
+ * The Billing Project licenses this file to you under the Apache License, version 2.0
  * (the "License"); you may not use this file except in compliance with the
  * License.  You may obtain a copy of the License at:
  *
@@ -20,46 +22,46 @@ import java.util.UUID;
 
 import javax.inject.Named;
 
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
 import org.killbill.billing.ObjectType;
-import org.killbill.bus.api.BusEvent;
-import org.killbill.clock.Clock;
+import org.killbill.billing.callcontext.InternalCallContext;
+import org.killbill.billing.events.ControlTagCreationInternalEvent;
+import org.killbill.billing.events.ControlTagDeletionInternalEvent;
+import org.killbill.billing.events.InvoiceAdjustmentInternalEvent;
+import org.killbill.billing.events.PaymentErrorInternalEvent;
+import org.killbill.billing.events.PaymentInfoInternalEvent;
+import org.killbill.billing.overdue.config.DefaultOverdueState;
+import org.killbill.billing.overdue.config.OverdueConfig;
+import org.killbill.billing.overdue.glue.DefaultOverdueModule;
 import org.killbill.billing.overdue.notification.OverdueAsyncBusNotificationKey;
 import org.killbill.billing.overdue.notification.OverdueAsyncBusNotificationKey.OverdueAsyncBusNotificationAction;
 import org.killbill.billing.overdue.notification.OverdueAsyncBusNotifier;
 import org.killbill.billing.overdue.notification.OverduePoster;
-import org.killbill.billing.overdue.glue.DefaultOverdueModule;
 import org.killbill.billing.util.callcontext.CallOrigin;
-import org.killbill.billing.callcontext.InternalCallContext;
 import org.killbill.billing.util.callcontext.InternalCallContextFactory;
 import org.killbill.billing.util.callcontext.UserType;
-import org.killbill.billing.events.ControlTagCreationInternalEvent;
-import org.killbill.billing.events.ControlTagDeletionInternalEvent;
-import org.killbill.billing.events.InvoiceAdjustmentInternalEvent;
-import org.killbill.billing.events.PaymentErrorInternalEvent;
-import org.killbill.billing.events.PaymentInfoInternalEvent;
 import org.killbill.billing.util.tag.ControlTagType;
+import org.killbill.bus.api.BusEvent;
+import org.killbill.clock.Clock;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 import com.google.common.eventbus.Subscribe;
 import com.google.inject.Inject;
 
 public class OverdueListener {
 
-    private final OverdueDispatcher dispatcher;
     private final InternalCallContextFactory internalCallContextFactory;
     private final OverduePoster asyncPoster;
     private final Clock clock;
 
+    private OverdueConfig config;
+
     private static final Logger log = LoggerFactory.getLogger(OverdueListener.class);
 
     @Inject
-    public OverdueListener(final OverdueDispatcher dispatcher,
-                           final Clock clock,
-                           @Named(DefaultOverdueModule.OVERDUE_NOTIFIER_ASYNC_BUS_NAMED)final OverduePoster asyncPoster,
+    public OverdueListener(final Clock clock,
+                           @Named(DefaultOverdueModule.OVERDUE_NOTIFIER_ASYNC_BUS_NAMED) final OverduePoster asyncPoster,
                            final InternalCallContextFactory internalCallContextFactory) {
-        this.dispatcher = dispatcher;
         this.asyncPoster = asyncPoster;
         this.clock = clock;
         this.internalCallContextFactory = internalCallContextFactory;
@@ -79,7 +81,6 @@ public class OverdueListener {
         }
     }
 
-
     @Subscribe
     public void handlePaymentInfoEvent(final PaymentInfoInternalEvent event) {
         log.debug("Received PaymentInfo event {}", event);
@@ -99,12 +100,34 @@ public class OverdueListener {
     }
 
     private void insertBusEventIntoNotificationQueue(final UUID accountId, final BusEvent event, final OverdueAsyncBusNotificationAction action) {
-        final OverdueAsyncBusNotificationKey notificationKey = new OverdueAsyncBusNotificationKey(accountId, action);
-        asyncPoster.insertOverdueNotification(accountId, clock.getUTCNow(), OverdueAsyncBusNotifier.OVERDUE_ASYNC_BUS_NOTIFIER_QUEUE, notificationKey, createCallContext(event.getUserToken(), event.getSearchKey1(), event.getSearchKey2()));
+        final boolean shouldInsertNotification = shouldInsertNotification();
 
+        if (shouldInsertNotification) {
+            final OverdueAsyncBusNotificationKey notificationKey = new OverdueAsyncBusNotificationKey(accountId, action);
+            asyncPoster.insertOverdueNotification(accountId, clock.getUTCNow(), OverdueAsyncBusNotifier.OVERDUE_ASYNC_BUS_NOTIFIER_QUEUE, notificationKey, createCallContext(event.getUserToken(), event.getSearchKey1(), event.getSearchKey2()));
+        }
+    }
+
+    // Optimization: don't bother running the Overdue machinery if it's disabled
+    private boolean shouldInsertNotification() {
+        if (config == null || config.getStateSet() == null || config.getStateSet().getStates() == null) {
+            return false;
+        }
+
+        for (final DefaultOverdueState state : config.getStateSet().getStates()) {
+            if (state.getCondition() != null) {
+                return true;
+            }
+        }
+
+        return false;
     }
 
     private InternalCallContext createCallContext(final UUID userToken, final Long accountRecordId, final Long tenantRecordId) {
         return internalCallContextFactory.createInternalCallContext(tenantRecordId, accountRecordId, "OverdueService", CallOrigin.INTERNAL, UserType.SYSTEM, userToken);
     }
+
+    public void setOverdueConfig(final OverdueConfig config) {
+        this.config = config;
+    }
 }
diff --git a/overdue/src/main/java/org/killbill/billing/overdue/notification/DefaultOverduePosterBase.java b/overdue/src/main/java/org/killbill/billing/overdue/notification/DefaultOverduePosterBase.java
index 42e54a7..30de6f4 100644
--- a/overdue/src/main/java/org/killbill/billing/overdue/notification/DefaultOverduePosterBase.java
+++ b/overdue/src/main/java/org/killbill/billing/overdue/notification/DefaultOverduePosterBase.java
@@ -1,7 +1,9 @@
 /*
  * Copyright 2010-2013 Ning, Inc.
+ * Copyright 2014 Groupon, Inc
+ * Copyright 2014 The Billing Project, LLC
  *
- * Ning licenses this file to you under the Apache License, version 2.0
+ * The Billing Project licenses this file to you under the Apache License, version 2.0
  * (the "License"); you may not use this file except in compliance with the
  * License.  You may obtain a copy of the License at:
  *
@@ -17,20 +19,10 @@
 package org.killbill.billing.overdue.notification;
 
 import java.util.Collection;
-import java.util.List;
 import java.util.UUID;
 
 import org.joda.time.DateTime;
-import org.skife.jdbi.v2.IDBI;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
 import org.killbill.billing.callcontext.InternalCallContext;
-import org.killbill.clock.Clock;
-import org.killbill.notificationq.api.NotificationEventWithMetadata;
-import org.killbill.notificationq.api.NotificationQueue;
-import org.killbill.notificationq.api.NotificationQueueService;
-import org.killbill.notificationq.api.NotificationQueueService.NoSuchNotificationQueue;
 import org.killbill.billing.overdue.service.DefaultOverdueService;
 import org.killbill.billing.util.cache.CacheControllerDispatcher;
 import org.killbill.billing.util.dao.NonEntityDao;
@@ -38,6 +30,14 @@ import org.killbill.billing.util.entity.dao.EntitySqlDao;
 import org.killbill.billing.util.entity.dao.EntitySqlDaoTransactionWrapper;
 import org.killbill.billing.util.entity.dao.EntitySqlDaoTransactionalJdbiWrapper;
 import org.killbill.billing.util.entity.dao.EntitySqlDaoWrapperFactory;
+import org.killbill.clock.Clock;
+import org.killbill.notificationq.api.NotificationEventWithMetadata;
+import org.killbill.notificationq.api.NotificationQueue;
+import org.killbill.notificationq.api.NotificationQueueService;
+import org.killbill.notificationq.api.NotificationQueueService.NoSuchNotificationQueue;
+import org.skife.jdbi.v2.IDBI;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 import com.google.common.annotations.VisibleForTesting;
 
@@ -57,7 +57,6 @@ public abstract class DefaultOverduePosterBase implements OverduePoster {
 
     @Override
     public <T extends OverdueCheckNotificationKey> void insertOverdueNotification(final UUID accountId, final DateTime futureNotificationTime, final String overdueQueueName, final T notificationKey, final InternalCallContext context) {
-
         try {
             final NotificationQueue overdueQueue = notificationQueueService.getNotificationQueue(DefaultOverdueService.OVERDUE_SERVICE_NAME,
                                                                                                  overdueQueueName);
@@ -65,13 +64,12 @@ public abstract class DefaultOverduePosterBase implements OverduePoster {
             transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<Void>() {
                 @Override
                 public Void inTransaction(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory) throws Exception {
-
                     // Check if we already have notifications for that key
                     final Class<T> clazz = (Class<T>) notificationKey.getClass();
-                    final Collection<NotificationEventWithMetadata<T>> futureNotifications = getFutureNotificationsForAccountInTransaction(entitySqlDaoWrapperFactory, overdueQueue, accountId,
+                    final Collection<NotificationEventWithMetadata<T>> futureNotifications = getFutureNotificationsForAccountInTransaction(entitySqlDaoWrapperFactory, overdueQueue,
                                                                                                                                            clazz, context);
 
-                    boolean shouldInsertNewNotification = cleanupFutureNotificationsFormTransaction(entitySqlDaoWrapperFactory, futureNotifications, futureNotificationTime, overdueQueue);
+                    final boolean shouldInsertNewNotification = cleanupFutureNotificationsFormTransaction(entitySqlDaoWrapperFactory, futureNotifications, futureNotificationTime, overdueQueue);
                     if (shouldInsertNewNotification) {
                         log.debug("Queuing overdue check notification. Account id: {}, timestamp: {}", accountId.toString(), futureNotificationTime.toString());
                         overdueQueue.recordFutureNotificationFromTransaction(entitySqlDaoWrapperFactory.getSqlDao(), futureNotificationTime, notificationKey, context.getUserToken(), context.getAccountRecordId(), context.getTenantRecordId());
@@ -81,13 +79,11 @@ public abstract class DefaultOverduePosterBase implements OverduePoster {
                     return null;
                 }
             });
-        } catch (NoSuchNotificationQueue e) {
+        } catch (final NoSuchNotificationQueue e) {
             log.error("Attempting to put items on a non-existent queue (DefaultOverdueCheck).", e);
-            return;
         }
     }
 
-
     @Override
     public <T extends OverdueCheckNotificationKey> void clearOverdueCheckNotifications(final UUID accountId, final String overdueQueueName, final Class<T> clazz, final InternalCallContext context) {
         try {
@@ -96,15 +92,16 @@ public abstract class DefaultOverduePosterBase implements OverduePoster {
             transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<Void>() {
                 @Override
                 public Void inTransaction(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory) throws Exception {
-                    final Collection<NotificationEventWithMetadata<T>> futureNotifications = getFutureNotificationsForAccountInTransaction(entitySqlDaoWrapperFactory, checkOverdueQueue, accountId,
+                    final Collection<NotificationEventWithMetadata<T>> futureNotifications = getFutureNotificationsForAccountInTransaction(entitySqlDaoWrapperFactory, checkOverdueQueue,
                                                                                                                                            clazz, context);
                     for (final NotificationEventWithMetadata<T> notification : futureNotifications) {
                         checkOverdueQueue.removeNotificationFromTransaction(entitySqlDaoWrapperFactory.getSqlDao(), notification.getRecordId());
                     }
+
                     return null;
                 }
             });
-        } catch (NoSuchNotificationQueue e) {
+        } catch (final NoSuchNotificationQueue e) {
             log.error("Attempting to clear items from a non-existent queue (DefaultOverdueCheck).", e);
         }
     }
@@ -112,29 +109,13 @@ public abstract class DefaultOverduePosterBase implements OverduePoster {
     @VisibleForTesting
     <T extends OverdueCheckNotificationKey> Collection<NotificationEventWithMetadata<T>> getFutureNotificationsForAccountInTransaction(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory,
                                                                                                                                        final NotificationQueue checkOverdueQueue,
-                                                                                                                                       final UUID accountId,
                                                                                                                                        final Class<T> clazz,
                                                                                                                                        final InternalCallContext context) {
-
-        final List<NotificationEventWithMetadata<T>> notifications = checkOverdueQueue.getFutureNotificationFromTransactionForSearchKey1(clazz, context.getAccountRecordId(), entitySqlDaoWrapperFactory.getSqlDao());
-
-        /*
-        final Collection<NotificationEventWithMetadata<T>> notificationsFiltered = Collections2.filter(notifications, new Predicate<NotificationEventWithMetadata<T>>() {
-            @Override
-            public boolean apply(@Nullable final NotificationEventWithMetadata<T> input) {
-                final OverdueCheckNotificationKey notificationKey = input.getEvent();
-                return (accountId.equals(notificationKey.getUuidKey()));
-            }
-        });
-        return notificationsFiltered;
-        */
-        return notifications;
+        return checkOverdueQueue.getFutureNotificationFromTransactionForSearchKey1(clazz, context.getAccountRecordId(), entitySqlDaoWrapperFactory.getSqlDao());
     }
 
-
     protected abstract <T extends OverdueCheckNotificationKey> boolean cleanupFutureNotificationsFormTransaction(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory,
-                                                                                                      final Collection<NotificationEventWithMetadata<T>> futureNotifications,
-                                                                                                      final DateTime futureNotificationTime, final NotificationQueue overdueQueue);
-
+                                                                                                                 final Collection<NotificationEventWithMetadata<T>> futureNotifications,
+                                                                                                                 final DateTime futureNotificationTime, final NotificationQueue overdueQueue);
 
 }
diff --git a/overdue/src/main/java/org/killbill/billing/overdue/notification/OverdueAsyncBusPoster.java b/overdue/src/main/java/org/killbill/billing/overdue/notification/OverdueAsyncBusPoster.java
index 0a29f57..750c9ee 100644
--- a/overdue/src/main/java/org/killbill/billing/overdue/notification/OverdueAsyncBusPoster.java
+++ b/overdue/src/main/java/org/killbill/billing/overdue/notification/OverdueAsyncBusPoster.java
@@ -1,7 +1,9 @@
 /*
  * Copyright 2010-2013 Ning, Inc.
+ * Copyright 2014 Groupon, Inc
+ * Copyright 2014 The Billing Project, LLC
  *
- * Ning licenses this file to you under the Apache License, version 2.0
+ * The Billing Project licenses this file to you under the Apache License, version 2.0
  * (the "License"); you may not use this file except in compliance with the
  * License.  You may obtain a copy of the License at:
  *
@@ -19,16 +21,15 @@ package org.killbill.billing.overdue.notification;
 import java.util.Collection;
 
 import org.joda.time.DateTime;
-import org.skife.jdbi.v2.IDBI;
-
-import org.killbill.clock.Clock;
-import org.killbill.notificationq.api.NotificationEventWithMetadata;
-import org.killbill.notificationq.api.NotificationQueue;
-import org.killbill.notificationq.api.NotificationQueueService;
 import org.killbill.billing.util.cache.CacheControllerDispatcher;
 import org.killbill.billing.util.dao.NonEntityDao;
 import org.killbill.billing.util.entity.dao.EntitySqlDao;
 import org.killbill.billing.util.entity.dao.EntitySqlDaoWrapperFactory;
+import org.killbill.clock.Clock;
+import org.killbill.notificationq.api.NotificationEventWithMetadata;
+import org.killbill.notificationq.api.NotificationQueue;
+import org.killbill.notificationq.api.NotificationQueueService;
+import org.skife.jdbi.v2.IDBI;
 
 import com.google.inject.Inject;
 
@@ -49,8 +50,6 @@ public class OverdueAsyncBusPoster extends DefaultOverduePosterBase {
         // If we already have notification for that account we don't insert the new one
         // Note that this is slightly incorrect because we could for instance already have a REFRESH and insert a CLEAR, but if that were the case,
         // if means overdue state would change very rapidly and the behavior would anyway be non deterministic
-        //
-        return futureNotifications.size() == 0;
+        return futureNotifications.isEmpty();
     }
-
 }
diff --git a/overdue/src/main/java/org/killbill/billing/overdue/notification/OverdueCheckPoster.java b/overdue/src/main/java/org/killbill/billing/overdue/notification/OverdueCheckPoster.java
index f01094f..308de2c 100644
--- a/overdue/src/main/java/org/killbill/billing/overdue/notification/OverdueCheckPoster.java
+++ b/overdue/src/main/java/org/killbill/billing/overdue/notification/OverdueCheckPoster.java
@@ -1,7 +1,9 @@
 /*
  * Copyright 2010-2013 Ning, Inc.
+ * Copyright 2014 Groupon, Inc
+ * Copyright 2014 The Billing Project, LLC
  *
- * Ning licenses this file to you under the Apache License, version 2.0
+ * The Billing Project licenses this file to you under the Apache License, version 2.0
  * (the "License"); you may not use this file except in compliance with the
  * License.  You may obtain a copy of the License at:
  *
@@ -17,19 +19,17 @@
 package org.killbill.billing.overdue.notification;
 
 import java.util.Collection;
-import java.util.Iterator;
 
 import org.joda.time.DateTime;
-import org.skife.jdbi.v2.IDBI;
-
-import org.killbill.clock.Clock;
-import org.killbill.notificationq.api.NotificationEventWithMetadata;
-import org.killbill.notificationq.api.NotificationQueue;
-import org.killbill.notificationq.api.NotificationQueueService;
 import org.killbill.billing.util.cache.CacheControllerDispatcher;
 import org.killbill.billing.util.dao.NonEntityDao;
 import org.killbill.billing.util.entity.dao.EntitySqlDao;
 import org.killbill.billing.util.entity.dao.EntitySqlDaoWrapperFactory;
+import org.killbill.clock.Clock;
+import org.killbill.notificationq.api.NotificationEventWithMetadata;
+import org.killbill.notificationq.api.NotificationQueue;
+import org.killbill.notificationq.api.NotificationQueueService;
+import org.skife.jdbi.v2.IDBI;
 
 import com.google.inject.Inject;
 
@@ -37,8 +37,8 @@ public class OverdueCheckPoster extends DefaultOverduePosterBase {
 
     @Inject
     public OverdueCheckPoster(final NotificationQueueService notificationQueueService,
-                                    final IDBI dbi, final Clock clock,
-                                    final CacheControllerDispatcher cacheControllerDispatcher, final NonEntityDao nonEntityDao) {
+                              final IDBI dbi, final Clock clock,
+                              final CacheControllerDispatcher cacheControllerDispatcher, final NonEntityDao nonEntityDao) {
         super(notificationQueueService, dbi, clock, cacheControllerDispatcher, nonEntityDao);
     }
 
@@ -48,7 +48,7 @@ public class OverdueCheckPoster extends DefaultOverduePosterBase {
                                                                                                         final DateTime futureNotificationTime, final NotificationQueue overdueQueue) {
 
         boolean shouldInsertNewNotification = true;
-        if (futureNotifications.size() > 0) {
+        if (!futureNotifications.isEmpty()) {
             // Results are ordered by effective date asc
             final DateTime earliestExistingNotificationDate = futureNotifications.iterator().next().getEffectiveDate();
 
@@ -63,9 +63,7 @@ public class OverdueCheckPoster extends DefaultOverduePosterBase {
             }
 
             int index = 0;
-            final Iterator<NotificationEventWithMetadata<T>> it = futureNotifications.iterator();
-            while (it.hasNext()) {
-                final NotificationEventWithMetadata<T> cur = it.next();
+            for (final NotificationEventWithMetadata<T> cur : futureNotifications) {
                 if (minIndexToDeleteFrom <= index) {
                     overdueQueue.removeNotificationFromTransaction(entitySqlDaoWrapperFactory.getSqlDao(), cur.getRecordId());
                 }
@@ -74,5 +72,4 @@ public class OverdueCheckPoster extends DefaultOverduePosterBase {
         }
         return shouldInsertNewNotification;
     }
-
 }
diff --git a/overdue/src/main/java/org/killbill/billing/overdue/notification/OverduePoster.java b/overdue/src/main/java/org/killbill/billing/overdue/notification/OverduePoster.java
index 517f17b..7a0949f 100644
--- a/overdue/src/main/java/org/killbill/billing/overdue/notification/OverduePoster.java
+++ b/overdue/src/main/java/org/killbill/billing/overdue/notification/OverduePoster.java
@@ -1,7 +1,9 @@
 /*
  * Copyright 2010-2013 Ning, Inc.
+ * Copyright 2014 Groupon, Inc
+ * Copyright 2014 The Billing Project, LLC
  *
- * Ning licenses this file to you under the Apache License, version 2.0
+ * The Billing Project licenses this file to you under the Apache License, version 2.0
  * (the "License"); you may not use this file except in compliance with the
  * License.  You may obtain a copy of the License at:
  *
@@ -19,12 +21,11 @@ package org.killbill.billing.overdue.notification;
 import java.util.UUID;
 
 import org.joda.time.DateTime;
-
 import org.killbill.billing.callcontext.InternalCallContext;
 
 public interface OverduePoster {
 
-    public  <T extends OverdueCheckNotificationKey> void insertOverdueNotification(final UUID accountId, final DateTime futureNotificationTime, final String overdueQueueName, final T notificationKey, final InternalCallContext context);
+    public <T extends OverdueCheckNotificationKey> void insertOverdueNotification(final UUID accountId, final DateTime futureNotificationTime, final String overdueQueueName, final T notificationKey, final InternalCallContext context);
 
-    public  <T extends OverdueCheckNotificationKey> void clearOverdueCheckNotifications(UUID accountId, final String overdueQueueName, final Class<T> clazz, final InternalCallContext context);
+    public <T extends OverdueCheckNotificationKey> void clearOverdueCheckNotifications(UUID accountId, final String overdueQueueName, final Class<T> clazz, final InternalCallContext context);
 }
diff --git a/overdue/src/main/java/org/killbill/billing/overdue/service/DefaultOverdueService.java b/overdue/src/main/java/org/killbill/billing/overdue/service/DefaultOverdueService.java
index e2e01a4..b60fc57 100644
--- a/overdue/src/main/java/org/killbill/billing/overdue/service/DefaultOverdueService.java
+++ b/overdue/src/main/java/org/killbill/billing/overdue/service/DefaultOverdueService.java
@@ -96,20 +96,24 @@ public class DefaultOverdueService implements OverdueService {
                 overdueConfig = XMLLoader.getObjectFromUri(u, OverdueConfig.class);
                 // File not found?
                 if (overdueConfig == null) {
-                    log.warn("Unable to load the overdue config from " + properties.getConfigURI());
+                    log.warn("Overdue system disabled: unable to load the overdue config from " + properties.getConfigURI());
                     overdueConfig = new OverdueConfig();
                 }
 
                 isConfigLoaded = true;
             } catch (final URISyntaxException e) {
+                log.warn("Overdue system disabled: unable to load the overdue config from " + properties.getConfigURI(), e);
                 overdueConfig = new OverdueConfig();
             } catch (final IllegalArgumentException e) {
+                log.warn("Overdue system disabled: unable to load the overdue config from " + properties.getConfigURI(), e);
                 overdueConfig = new OverdueConfig();
             } catch (final Exception e) {
+                log.warn("Unable to load the overdue config from " + properties.getConfigURI(), e);
                 throw new ServiceException(e);
             }
 
             factory.setOverdueConfig(overdueConfig);
+            listener.setOverdueConfig(overdueConfig);
             ((DefaultOverdueUserApi) userApi).setOverdueConfig(overdueConfig);
         }
     }
diff --git a/overdue/src/main/java/org/killbill/billing/overdue/wrapper/OverdueWrapperFactory.java b/overdue/src/main/java/org/killbill/billing/overdue/wrapper/OverdueWrapperFactory.java
index 72e6250..5f8323b 100644
--- a/overdue/src/main/java/org/killbill/billing/overdue/wrapper/OverdueWrapperFactory.java
+++ b/overdue/src/main/java/org/killbill/billing/overdue/wrapper/OverdueWrapperFactory.java
@@ -87,7 +87,7 @@ public class OverdueWrapperFactory {
 
                 @SuppressWarnings("unchecked")
                 @Override
-                protected DefaultOverdueState[] getStates() {
+                public DefaultOverdueState[] getStates() {
                     return new DefaultOverdueState[0];
                 }
 
diff --git a/overdue/src/main/resources/NoOverdueConfig.xml b/overdue/src/main/resources/NoOverdueConfig.xml
new file mode 100644
index 0000000..b70da8d
--- /dev/null
+++ b/overdue/src/main/resources/NoOverdueConfig.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ Copyright 2010-2013 Ning, Inc.
+  ~ 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.
+  -->
+
+<overdueConfig xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+               xsi:noNamespaceSchemaLocation="CatalogSchema.xsd ">
+    <accountOverdueStates>
+        <state name="Clear">
+            <isClearState>true</isClearState>
+        </state>
+    </accountOverdueStates>
+</overdueConfig>
diff --git a/overdue/src/test/java/org/killbill/billing/overdue/notification/TestDefaultOverdueCheckPoster.java b/overdue/src/test/java/org/killbill/billing/overdue/notification/TestDefaultOverdueCheckPoster.java
index d128df7..819fc16 100644
--- a/overdue/src/test/java/org/killbill/billing/overdue/notification/TestDefaultOverdueCheckPoster.java
+++ b/overdue/src/test/java/org/killbill/billing/overdue/notification/TestDefaultOverdueCheckPoster.java
@@ -30,9 +30,6 @@ import org.killbill.billing.account.api.Account;
 import org.killbill.notificationq.api.NotificationEventWithMetadata;
 import org.killbill.notificationq.api.NotificationQueue;
 import org.killbill.billing.overdue.OverdueTestSuiteWithEmbeddedDB;
-import org.killbill.billing.overdue.notification.OverdueCheckNotificationKey;
-import org.killbill.billing.overdue.notification.OverdueCheckNotifier;
-import org.killbill.billing.overdue.notification.OverdueCheckPoster;
 import org.killbill.billing.overdue.service.DefaultOverdueService;
 import org.killbill.billing.util.entity.dao.EntitySqlDao;
 import org.killbill.billing.util.entity.dao.EntitySqlDaoTransactionWrapper;
@@ -92,7 +89,7 @@ public class TestDefaultOverdueCheckPoster extends OverdueTestSuiteWithEmbeddedD
         return entitySqlDaoTransactionalJdbiWrapper.execute(new EntitySqlDaoTransactionWrapper<Collection<NotificationEventWithMetadata<OverdueCheckNotificationKey>>>() {
             @Override
             public Collection<NotificationEventWithMetadata<OverdueCheckNotificationKey>> inTransaction(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory) throws Exception {
-                return ((OverdueCheckPoster)checkPoster).getFutureNotificationsForAccountInTransaction(entitySqlDaoWrapperFactory, overdueQueue, account.getId(), OverdueCheckNotificationKey.class, internalCallContext);
+                return ((OverdueCheckPoster)checkPoster).getFutureNotificationsForAccountInTransaction(entitySqlDaoWrapperFactory, overdueQueue, OverdueCheckNotificationKey.class, internalCallContext);
             }
         });
     }
diff --git a/payment/src/main/java/org/killbill/billing/payment/api/DefaultPaymentApi.java b/payment/src/main/java/org/killbill/billing/payment/api/DefaultPaymentApi.java
index 6fb9f44..3c0b151 100644
--- a/payment/src/main/java/org/killbill/billing/payment/api/DefaultPaymentApi.java
+++ b/payment/src/main/java/org/killbill/billing/payment/api/DefaultPaymentApi.java
@@ -29,8 +29,8 @@ import org.killbill.billing.ErrorCode;
 import org.killbill.billing.account.api.Account;
 import org.killbill.billing.callcontext.InternalCallContext;
 import org.killbill.billing.catalog.api.Currency;
-import org.killbill.billing.payment.core.PaymentProcessor;
 import org.killbill.billing.payment.core.PaymentMethodProcessor;
+import org.killbill.billing.payment.core.PaymentProcessor;
 import org.killbill.billing.payment.core.PluginControlledPaymentProcessor;
 import org.killbill.billing.util.callcontext.CallContext;
 import org.killbill.billing.util.callcontext.InternalCallContextFactory;
@@ -60,10 +60,9 @@ public class DefaultPaymentApi implements PaymentApi {
         this.internalCallContextFactory = internalCallContextFactory;
     }
 
-
     @Override
     public Payment createAuthorization(final Account account, final UUID paymentMethodId, @Nullable final UUID paymentId, final BigDecimal amount, final Currency currency, @Nullable final String paymentExternalKey, @Nullable final String paymentTransactionExternalKey,
-                                             final Iterable<PluginProperty> properties, final CallContext callContext) throws PaymentApiException {
+                                       final Iterable<PluginProperty> properties, final CallContext callContext) throws PaymentApiException {
 
         checkNotNullParameter(account, "account");
         checkNotNullParameter(paymentMethodId, "paymentMethodId");
@@ -76,12 +75,12 @@ public class DefaultPaymentApi implements PaymentApi {
 
         final InternalCallContext internalCallContext = internalCallContextFactory.createInternalCallContext(account.getId(), callContext);
         return paymentProcessor.createAuthorization(IS_API_PAYMENT, NULL_ATTEMPT_ID, account, paymentMethodId, paymentId, amount, currency, paymentExternalKey, paymentTransactionExternalKey,
-                                                          SHOULD_LOCK_ACCOUNT, properties, callContext, internalCallContext);
+                                                    SHOULD_LOCK_ACCOUNT, properties, callContext, internalCallContext);
     }
 
     @Override
     public Payment createCapture(final Account account, final UUID paymentId, final BigDecimal amount, final Currency currency, @Nullable final String paymentTransactionExternalKey,
-                                       final Iterable<PluginProperty> properties, final CallContext callContext) throws PaymentApiException {
+                                 final Iterable<PluginProperty> properties, final CallContext callContext) throws PaymentApiException {
 
         checkNotNullParameter(account, "account");
         checkNotNullParameter(paymentId, "paymentId");
@@ -93,12 +92,12 @@ public class DefaultPaymentApi implements PaymentApi {
 
         final InternalCallContext internalCallContext = internalCallContextFactory.createInternalCallContext(account.getId(), callContext);
         return paymentProcessor.createCapture(IS_API_PAYMENT, NULL_ATTEMPT_ID, account, paymentId, amount, currency, paymentTransactionExternalKey,
-                                                    SHOULD_LOCK_ACCOUNT, properties, callContext, internalCallContext);
+                                              SHOULD_LOCK_ACCOUNT, properties, callContext, internalCallContext);
     }
 
     @Override
     public Payment createPurchase(final Account account, final UUID paymentMethodId, @Nullable final UUID paymentId, final BigDecimal amount, final Currency currency, @Nullable final String paymentExternalKey, @Nullable final String paymentTransactionExternalKey,
-                                        final Iterable<PluginProperty> properties, final CallContext callContext) throws PaymentApiException {
+                                  final Iterable<PluginProperty> properties, final CallContext callContext) throws PaymentApiException {
 
         checkNotNullParameter(account, "account");
         checkNotNullParameter(paymentMethodId, "paymentMethodId");
@@ -111,12 +110,12 @@ public class DefaultPaymentApi implements PaymentApi {
 
         final InternalCallContext internalCallContext = internalCallContextFactory.createInternalCallContext(account.getId(), callContext);
         return paymentProcessor.createPurchase(IS_API_PAYMENT, NULL_ATTEMPT_ID, account, paymentMethodId, paymentId, amount, currency, paymentExternalKey, paymentTransactionExternalKey,
-                                                     SHOULD_LOCK_ACCOUNT, properties, callContext, internalCallContext);
+                                               SHOULD_LOCK_ACCOUNT, properties, callContext, internalCallContext);
     }
 
     @Override
     public Payment createPurchaseWithPaymentControl(final Account account, @Nullable final UUID paymentMethodId, @Nullable final UUID paymentId, final BigDecimal amount, final Currency currency, final String paymentExternalKey, final String paymentTransactionExternalKey,
-                                                          final Iterable<PluginProperty> properties, final PaymentOptions paymentOptions, final CallContext callContext) throws PaymentApiException {
+                                                    final Iterable<PluginProperty> properties, final PaymentOptions paymentOptions, final CallContext callContext) throws PaymentApiException {
 
         checkNotNullParameter(account, "account");
         checkNotNullParameter(amount, "amount");
@@ -144,7 +143,7 @@ public class DefaultPaymentApi implements PaymentApi {
 
     @Override
     public Payment createVoid(final Account account, final UUID paymentId, @Nullable final String paymentTransactionExternalKey, final Iterable<PluginProperty> properties,
-                                    final CallContext callContext) throws PaymentApiException {
+                              final CallContext callContext) throws PaymentApiException {
 
         checkNotNullParameter(account, "account");
         checkNotNullParameter(paymentId, "paymentId");
@@ -154,13 +153,13 @@ public class DefaultPaymentApi implements PaymentApi {
 
         final InternalCallContext internalCallContext = internalCallContextFactory.createInternalCallContext(account.getId(), callContext);
         return paymentProcessor.createVoid(IS_API_PAYMENT, NULL_ATTEMPT_ID, account, paymentId, paymentTransactionExternalKey,
-                                                 SHOULD_LOCK_ACCOUNT, properties, callContext, internalCallContext);
+                                           SHOULD_LOCK_ACCOUNT, properties, callContext, internalCallContext);
 
     }
 
     @Override
     public Payment createRefund(final Account account, final UUID paymentId, final BigDecimal amount, final Currency currency, @Nullable final String paymentTransactionExternalKey, final Iterable<PluginProperty> properties,
-                                      final CallContext callContext) throws PaymentApiException {
+                                final CallContext callContext) throws PaymentApiException {
 
         checkNotNullParameter(account, "account");
         checkNotNullParameter(amount, "amount");
@@ -173,12 +172,12 @@ public class DefaultPaymentApi implements PaymentApi {
 
         final InternalCallContext internalCallContext = internalCallContextFactory.createInternalCallContext(account.getId(), callContext);
         return paymentProcessor.createRefund(IS_API_PAYMENT, NULL_ATTEMPT_ID, account, paymentId, amount, currency, paymentTransactionExternalKey,
-                                                   SHOULD_LOCK_ACCOUNT, properties, callContext, internalCallContext);
+                                             SHOULD_LOCK_ACCOUNT, properties, callContext, internalCallContext);
     }
 
     @Override
     public Payment createRefundWithPaymentControl(final Account account, final UUID paymentId, @Nullable final BigDecimal amount, final Currency currency, final String paymentTransactionExternalKey, final Iterable<PluginProperty> properties,
-                                                        final PaymentOptions paymentOptions, final CallContext callContext) throws PaymentApiException {
+                                                  final PaymentOptions paymentOptions, final CallContext callContext) throws PaymentApiException {
 
         checkNotNullParameter(account, "account");
         checkNotNullParameter(currency, "currency");
@@ -199,8 +198,8 @@ public class DefaultPaymentApi implements PaymentApi {
 
     @Override
     public Payment createCredit(final Account account, final UUID paymentMethodId, @Nullable final UUID paymentId, final BigDecimal amount, final Currency currency,
-                                      @Nullable final String paymentExternalKey, @Nullable  final String paymentTransactionExternalKey,
-                                      final Iterable<PluginProperty> properties, final CallContext callContext) throws PaymentApiException {
+                                @Nullable final String paymentExternalKey, @Nullable final String paymentTransactionExternalKey,
+                                final Iterable<PluginProperty> properties, final CallContext callContext) throws PaymentApiException {
 
         checkNotNullParameter(account, "account");
         checkNotNullParameter(paymentMethodId, "paymentMethodId");
@@ -213,7 +212,7 @@ public class DefaultPaymentApi implements PaymentApi {
 
         final InternalCallContext internalCallContext = internalCallContextFactory.createInternalCallContext(account.getId(), callContext);
         return paymentProcessor.createCredit(IS_API_PAYMENT, NULL_ATTEMPT_ID, account, paymentMethodId, paymentId, amount, currency, paymentExternalKey, paymentTransactionExternalKey,
-                                                   SHOULD_LOCK_ACCOUNT, properties, callContext, internalCallContext);
+                                             SHOULD_LOCK_ACCOUNT, properties, callContext, internalCallContext);
 
     }
 
@@ -234,7 +233,6 @@ public class DefaultPaymentApi implements PaymentApi {
         throw new IllegalStateException("Not implemented");
     }
 
-
     @Override
     public Payment createChargeback(final Account account, final UUID paymentId, final BigDecimal amount, final Currency currency, final String paymentTransactionExternalKey, final CallContext callContext) throws PaymentApiException {
         checkNotNullParameter(account, "account");
@@ -247,11 +245,10 @@ public class DefaultPaymentApi implements PaymentApi {
 
         final InternalCallContext internalCallContext = internalCallContextFactory.createInternalCallContext(account.getId(), callContext);
         return paymentProcessor.createChargeback(IS_API_PAYMENT, NULL_ATTEMPT_ID, account, paymentId, paymentTransactionExternalKey, amount, currency, true,
-                                                       callContext, internalCallContext);
+                                                 callContext, internalCallContext);
 
     }
 
-
     @Override
     public Payment createChargebackWithPaymentControl(final Account account, final UUID paymentId, final BigDecimal amount, final Currency currency, final String paymentTransactionExternalKey, final PaymentOptions paymentOptions, final CallContext callContext) throws PaymentApiException {
         checkNotNullParameter(account, "account");
@@ -267,17 +264,17 @@ public class DefaultPaymentApi implements PaymentApi {
 
     @Override
     public List<Payment> getAccountPayments(final UUID accountId, final boolean withPluginInfo, final Iterable<PluginProperty> properties, final TenantContext tenantContext) throws PaymentApiException {
-        return paymentProcessor.getAccountPayments(accountId, internalCallContextFactory.createInternalTenantContext(accountId, tenantContext));
+        return paymentProcessor.getAccountPayments(accountId, withPluginInfo, tenantContext, internalCallContextFactory.createInternalTenantContext(accountId, tenantContext));
     }
 
     @Override
     public Pagination<Payment> getPayments(final Long offset, final Long limit, final boolean withPluginInfo, final Iterable<PluginProperty> properties, final TenantContext context) {
-        return paymentProcessor.getPayments(offset, limit, properties, context, internalCallContextFactory.createInternalTenantContext(context));
+        return paymentProcessor.getPayments(offset, limit, withPluginInfo, properties, context, internalCallContextFactory.createInternalTenantContext(context));
     }
 
     @Override
     public Pagination<Payment> getPayments(final Long offset, final Long limit, final String pluginName, final boolean withPluginInfo, final Iterable<PluginProperty> properties, final TenantContext tenantContext) throws PaymentApiException {
-        return paymentProcessor.getPayments(offset, limit, pluginName, properties, tenantContext, internalCallContextFactory.createInternalTenantContext(tenantContext));
+        return paymentProcessor.getPayments(offset, limit, pluginName, withPluginInfo, properties, tenantContext, internalCallContextFactory.createInternalTenantContext(tenantContext));
     }
 
     @Override
@@ -299,15 +296,14 @@ public class DefaultPaymentApi implements PaymentApi {
         return payment;
     }
 
-    // TODO withPluginInfo needs to be honored...
     @Override
     public Pagination<Payment> searchPayments(final String searchKey, final Long offset, final Long limit, final boolean withPluginInfo, final Iterable<PluginProperty> properties, final TenantContext context) {
-        return paymentProcessor.searchPayments(searchKey, offset, limit, properties, context, internalCallContextFactory.createInternalTenantContext(context));
+        return paymentProcessor.searchPayments(searchKey, offset, limit, withPluginInfo, properties, context, internalCallContextFactory.createInternalTenantContext(context));
     }
 
     @Override
     public Pagination<Payment> searchPayments(final String searchKey, final Long offset, final Long limit, final String pluginName, final boolean withPluginInfo, final Iterable<PluginProperty> properties, final TenantContext context) throws PaymentApiException {
-        return paymentProcessor.searchPayments(searchKey, offset, limit, pluginName, properties, context, internalCallContextFactory.createInternalTenantContext(context));
+        return paymentProcessor.searchPayments(searchKey, offset, limit, pluginName, withPluginInfo, properties, context, internalCallContextFactory.createInternalTenantContext(context));
     }
 
     @Override
@@ -339,22 +335,22 @@ public class DefaultPaymentApi implements PaymentApi {
 
     @Override
     public Pagination<PaymentMethod> getPaymentMethods(final Long offset, final Long limit, final boolean withPluginInfo, final Iterable<PluginProperty> properties, final TenantContext context) {
-        return paymentMethodProcessor.getPaymentMethods(offset, limit, properties, context, internalCallContextFactory.createInternalTenantContext(context));
+        return paymentMethodProcessor.getPaymentMethods(offset, limit, withPluginInfo, properties, context, internalCallContextFactory.createInternalTenantContext(context));
     }
 
     @Override
     public Pagination<PaymentMethod> getPaymentMethods(final Long offset, final Long limit, final String pluginName, final boolean withPluginInfo, final Iterable<PluginProperty> properties, final TenantContext context) throws PaymentApiException {
-        return paymentMethodProcessor.getPaymentMethods(offset, limit, pluginName, properties, context, internalCallContextFactory.createInternalTenantContext(context));
+        return paymentMethodProcessor.getPaymentMethods(offset, limit, pluginName, withPluginInfo, properties, context, internalCallContextFactory.createInternalTenantContext(context));
     }
 
     @Override
     public Pagination<PaymentMethod> searchPaymentMethods(final String searchKey, final Long offset, final Long limit, final boolean withPluginInfo, final Iterable<PluginProperty> properties, final TenantContext context) {
-        return paymentMethodProcessor.searchPaymentMethods(searchKey, offset, limit, properties, context, internalCallContextFactory.createInternalTenantContext(context));
+        return paymentMethodProcessor.searchPaymentMethods(searchKey, offset, limit, withPluginInfo, properties, context, internalCallContextFactory.createInternalTenantContext(context));
     }
 
     @Override
     public Pagination<PaymentMethod> searchPaymentMethods(final String searchKey, final Long offset, final Long limit, final String pluginName, final boolean withPluginInfo, final Iterable<PluginProperty> properties, final TenantContext context) throws PaymentApiException {
-        return paymentMethodProcessor.searchPaymentMethods(searchKey, offset, limit, pluginName, properties, context, internalCallContextFactory.createInternalTenantContext(context));
+        return paymentMethodProcessor.searchPaymentMethods(searchKey, offset, limit, pluginName, withPluginInfo, properties, context, internalCallContextFactory.createInternalTenantContext(context));
     }
 
     @Override
@@ -388,7 +384,7 @@ public class DefaultPaymentApi implements PaymentApi {
         return paymentMethods;
     }
 
-    private void logAPICall(final String transactionType, final Account account, final UUID paymentMethodId, @Nullable final UUID paymentId,  @Nullable final UUID transactionId, @Nullable final BigDecimal amount, @Nullable final Currency currency, @Nullable final String paymentExternalKey, @Nullable final String paymentTransactionExternalKey) {
+    private void logAPICall(final String transactionType, final Account account, final UUID paymentMethodId, @Nullable final UUID paymentId, @Nullable final UUID transactionId, @Nullable final BigDecimal amount, @Nullable final Currency currency, @Nullable final String paymentExternalKey, @Nullable final String paymentTransactionExternalKey) {
         if (log.isInfoEnabled()) {
             final StringBuilder logLine = new StringBuilder();
             logLine.append("PaymentApi : ")
diff --git a/payment/src/main/java/org/killbill/billing/payment/core/PaymentMethodProcessor.java b/payment/src/main/java/org/killbill/billing/payment/core/PaymentMethodProcessor.java
index 71b5434..5dc84f1 100644
--- a/payment/src/main/java/org/killbill/billing/payment/core/PaymentMethodProcessor.java
+++ b/payment/src/main/java/org/killbill/billing/payment/core/PaymentMethodProcessor.java
@@ -19,6 +19,7 @@
 package org.killbill.billing.payment.core;
 
 import java.util.ArrayList;
+import java.util.Collection;
 import java.util.Collections;
 import java.util.List;
 import java.util.UUID;
@@ -59,7 +60,6 @@ import org.killbill.billing.util.entity.dao.DefaultPaginationHelper.EntityPagina
 import org.killbill.billing.util.entity.dao.DefaultPaginationHelper.SourcePaginationBuilder;
 import org.killbill.clock.Clock;
 import org.killbill.commons.locker.GlobalLocker;
-import org.killbill.commons.locker.LockFailedException;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -102,7 +102,7 @@ public class PaymentMethodProcessor extends ProcessorBase {
                 @Override
                 public PluginDispatcherReturnType<UUID> doOperation() throws PaymentApiException {
                     PaymentMethod pm = null;
-                    PaymentPluginApi pluginApi;
+                    final PaymentPluginApi pluginApi;
                     try {
                         pluginApi = getPaymentPluginApi(paymentPluginServiceName);
                         pm = new DefaultPaymentMethod(paymentMethodExternalKey, account.getId(), paymentPluginServiceName, paymentMethodProps);
@@ -124,7 +124,7 @@ public class PaymentMethodProcessor extends ProcessorBase {
                 }
             });
             return result.getReturnType();
-        } catch (Exception e) {
+        } catch (final Exception e) {
             throw new PaymentApiException(e, ErrorCode.PAYMENT_INTERNAL_ERROR, Objects.firstNonNull(e.getMessage(), ""));
         }
     }
@@ -135,17 +135,12 @@ public class PaymentMethodProcessor extends ProcessorBase {
 
     public List<PaymentMethod> getPaymentMethods(final UUID accountId, final boolean withPluginInfo, final Iterable<PluginProperty> properties, final TenantContext tenantContext, final InternalTenantContext context) throws PaymentApiException {
         final List<PaymentMethodModelDao> paymentMethodModels = paymentDao.getPaymentMethods(accountId, context);
-        if (paymentMethodModels.size() == 0) {
+        if (paymentMethodModels.isEmpty()) {
             return Collections.emptyList();
         }
         return getPaymentMethodInternal(paymentMethodModels, withPluginInfo, properties, tenantContext, context);
     }
 
-    public PaymentMethod getPaymentMethodById(final UUID paymentMethodId, final boolean includedDeleted, final boolean withPluginInfo, final Iterable<PluginProperty> properties, final InternalTenantContext context)
-            throws PaymentApiException {
-        return getPaymentMethodById(paymentMethodId, includedDeleted, withPluginInfo, properties, buildTenantContext(context), context);
-    }
-
     public PaymentMethod getPaymentMethodById(final UUID paymentMethodId, final boolean includedDeleted, final boolean withPluginInfo, final Iterable<PluginProperty> properties, final TenantContext tenantContext, final InternalTenantContext context)
             throws PaymentApiException {
         final PaymentMethodModelDao paymentMethodModel = includedDeleted ? paymentDao.getPaymentMethodIncludedDeleted(paymentMethodId, context) : paymentDao.getPaymentMethod(paymentMethodId, context);
@@ -182,21 +177,21 @@ public class PaymentMethodProcessor extends ProcessorBase {
         return new DefaultPaymentMethod(paymentMethodModelDao, paymentMethodPlugin);
     }
 
-    public Pagination<PaymentMethod> getPaymentMethods(final Long offset, final Long limit, final Iterable<PluginProperty> properties, final TenantContext tenantContext, final InternalTenantContext internalTenantContext) {
+    public Pagination<PaymentMethod> getPaymentMethods(final Long offset, final Long limit, final boolean withPluginInfo, final Iterable<PluginProperty> properties, final TenantContext tenantContext, final InternalTenantContext internalTenantContext) {
         return getEntityPaginationFromPlugins(getAvailablePlugins(),
                                               offset,
                                               limit,
                                               new EntityPaginationBuilder<PaymentMethod, PaymentApiException>() {
                                                   @Override
                                                   public Pagination<PaymentMethod> build(final Long offset, final Long limit, final String pluginName) throws PaymentApiException {
-                                                      return getPaymentMethods(offset, limit, pluginName, properties, tenantContext, internalTenantContext);
+                                                      return getPaymentMethods(offset, limit, pluginName, withPluginInfo, properties, tenantContext, internalTenantContext);
                                                   }
                                               }
                                              );
     }
 
-    public Pagination<PaymentMethod> getPaymentMethods(final Long offset, final Long limit, final String pluginName, final Iterable<PluginProperty> properties, final TenantContext tenantContext, final InternalTenantContext internalTenantContext) throws PaymentApiException {
-        final PaymentPluginApi pluginApi = getPaymentPluginApi(pluginName);
+    public Pagination<PaymentMethod> getPaymentMethods(final Long offset, final Long limit, final String pluginName, final boolean withPluginInfo, final Iterable<PluginProperty> properties, final TenantContext tenantContext, final InternalTenantContext internalTenantContext) throws PaymentApiException {
+        final PaymentPluginApi pluginApi = withPluginInfo ? getPaymentPluginApi(pluginName) : null;
 
         return getEntityPagination(limit,
                                    new SourcePaginationBuilder<PaymentMethodModelDao, PaymentApiException>() {
@@ -210,11 +205,13 @@ public class PaymentMethodProcessor extends ProcessorBase {
                                        @Override
                                        public PaymentMethod apply(final PaymentMethodModelDao paymentMethodModelDao) {
                                            PaymentMethodPlugin paymentMethodPlugin = null;
-                                           try {
-                                               paymentMethodPlugin = pluginApi.getPaymentMethodDetail(paymentMethodModelDao.getAccountId(), paymentMethodModelDao.getId(), properties, tenantContext);
-                                           } catch (final PaymentPluginApiException e) {
-                                               log.warn("Unable to find payment method id " + paymentMethodModelDao.getId() + " in plugin " + pluginName);
-                                               // We still want to return a payment method object, even though the plugin details are missing
+                                           if (pluginApi != null) {
+                                               try {
+                                                   paymentMethodPlugin = pluginApi.getPaymentMethodDetail(paymentMethodModelDao.getAccountId(), paymentMethodModelDao.getId(), properties, tenantContext);
+                                               } catch (final PaymentPluginApiException e) {
+                                                   log.warn("Unable to find payment method id " + paymentMethodModelDao.getId() + " in plugin " + pluginName);
+                                                   // We still want to return a payment method object, even though the plugin details are missing
+                                               }
                                            }
 
                                            return new DefaultPaymentMethod(paymentMethodModelDao, paymentMethodPlugin);
@@ -223,53 +220,70 @@ public class PaymentMethodProcessor extends ProcessorBase {
                                   );
     }
 
-    public Pagination<PaymentMethod> searchPaymentMethods(final String searchKey, final Long offset, final Long limit, final Iterable<PluginProperty> properties, final TenantContext tenantContext, final InternalTenantContext internalTenantContext) {
+    public Pagination<PaymentMethod> searchPaymentMethods(final String searchKey, final Long offset, final Long limit, final boolean withPluginInfo, final Iterable<PluginProperty> properties, final TenantContext tenantContext, final InternalTenantContext internalTenantContext) {
         return getEntityPaginationFromPlugins(getAvailablePlugins(),
                                               offset,
                                               limit,
                                               new EntityPaginationBuilder<PaymentMethod, PaymentApiException>() {
                                                   @Override
                                                   public Pagination<PaymentMethod> build(final Long offset, final Long limit, final String pluginName) throws PaymentApiException {
-                                                      return searchPaymentMethods(searchKey, offset, limit, pluginName, properties, tenantContext, internalTenantContext);
+                                                      return searchPaymentMethods(searchKey, offset, limit, pluginName, withPluginInfo, properties, tenantContext, internalTenantContext);
                                                   }
                                               }
                                              );
     }
 
     public Pagination<PaymentMethod> searchPaymentMethods(final String searchKey, final Long offset, final Long limit, final String pluginName,
-                                                          final Iterable<PluginProperty> properties, final TenantContext tenantContext, final InternalTenantContext internalTenantContext) throws PaymentApiException {
-        final PaymentPluginApi pluginApi = getPaymentPluginApi(pluginName);
-
-        return getEntityPagination(limit,
-                                   new SourcePaginationBuilder<PaymentMethodPlugin, PaymentApiException>() {
-                                       @Override
-                                       public Pagination<PaymentMethodPlugin> build() throws PaymentApiException {
-                                           try {
-                                               return pluginApi.searchPaymentMethods(searchKey, offset, limit, properties, tenantContext);
-                                           } catch (final PaymentPluginApiException e) {
-                                               throw new PaymentApiException(e, ErrorCode.PAYMENT_PLUGIN_SEARCH_PAYMENT_METHODS, pluginName, searchKey);
+                                                          final boolean withPluginInfo, final Iterable<PluginProperty> properties, final TenantContext tenantContext, final InternalTenantContext internalTenantContext) throws PaymentApiException {
+        if (withPluginInfo) {
+            final PaymentPluginApi pluginApi = getPaymentPluginApi(pluginName);
+
+            return getEntityPagination(limit,
+                                       new SourcePaginationBuilder<PaymentMethodPlugin, PaymentApiException>() {
+                                           @Override
+                                           public Pagination<PaymentMethodPlugin> build() throws PaymentApiException {
+                                               try {
+                                                   return pluginApi.searchPaymentMethods(searchKey, offset, limit, properties, tenantContext);
+                                               } catch (final PaymentPluginApiException e) {
+                                                   throw new PaymentApiException(e, ErrorCode.PAYMENT_PLUGIN_SEARCH_PAYMENT_METHODS, pluginName, searchKey);
+                                               }
+                                           }
+                                       },
+                                       new Function<PaymentMethodPlugin, PaymentMethod>() {
+                                           @Override
+                                           public PaymentMethod apply(final PaymentMethodPlugin paymentMethodPlugin) {
+                                               if (paymentMethodPlugin.getKbPaymentMethodId() == null) {
+                                                   // Garbage from the plugin?
+                                                   log.debug("Plugin {} returned a payment method without a kbPaymentMethodId for searchKey {}", pluginName, searchKey);
+                                                   return null;
+                                               }
+
+                                               final PaymentMethodModelDao paymentMethodModelDao = paymentDao.getPaymentMethodIncludedDeleted(paymentMethodPlugin.getKbPaymentMethodId(), internalTenantContext);
+                                               if (paymentMethodModelDao == null) {
+                                                   log.warn("Unable to find payment method id " + paymentMethodPlugin.getKbPaymentMethodId() + " present in plugin " + pluginName);
+                                                   return null;
+                                               }
+
+                                               return new DefaultPaymentMethod(paymentMethodModelDao, paymentMethodPlugin);
                                            }
                                        }
-                                   },
-                                   new Function<PaymentMethodPlugin, PaymentMethod>() {
-                                       @Override
-                                       public PaymentMethod apply(final PaymentMethodPlugin paymentMethodPlugin) {
-                                           if (paymentMethodPlugin.getKbPaymentMethodId() == null) {
-                                               // Garbage from the plugin?
-                                               log.debug("Plugin {} returned a payment method without a kbPaymentMethodId for searchKey {}", pluginName, searchKey);
-                                               return null;
+                                      );
+        } else {
+            return getEntityPagination(limit,
+                                       new SourcePaginationBuilder<PaymentMethodModelDao, PaymentApiException>() {
+                                           @Override
+                                           public Pagination<PaymentMethodModelDao> build() {
+                                               return paymentDao.searchPaymentMethods(searchKey, offset, limit, internalTenantContext);
                                            }
-
-                                           final PaymentMethodModelDao paymentMethodModelDao = paymentDao.getPaymentMethodIncludedDeleted(paymentMethodPlugin.getKbPaymentMethodId(), internalTenantContext);
-                                           if (paymentMethodModelDao == null) {
-                                               log.warn("Unable to find payment method id " + paymentMethodPlugin.getKbPaymentMethodId() + " present in plugin " + pluginName);
-                                               return null;
+                                       },
+                                       new Function<PaymentMethodModelDao, PaymentMethod>() {
+                                           @Override
+                                           public PaymentMethod apply(final PaymentMethodModelDao paymentMethodModelDao) {
+                                               return new DefaultPaymentMethod(paymentMethodModelDao, null);
                                            }
-
-                                           return new DefaultPaymentMethod(paymentMethodModelDao, paymentMethodPlugin);
                                        }
-                                   }
-                                  );
+                                      );
+        }
     }
 
     public PaymentMethod getExternalPaymentMethod(final UUID accountId, final Iterable<PluginProperty> properties, final TenantContext tenantContext, final InternalTenantContext context) throws PaymentApiException {
@@ -285,7 +299,7 @@ public class PaymentMethodProcessor extends ProcessorBase {
     public UUID createOrGetExternalPaymentMethod(final String paymentMethodExternalKey, final Account account, final Iterable<PluginProperty> properties, final CallContext callContext, final InternalCallContext context) throws PaymentApiException {
         // Check if this account has already used the external payment plugin
         // If not, it's the first time - add a payment method for it
-        PaymentMethod externalPaymentMethod = getExternalPaymentMethod(account.getId(), properties, callContext, context);
+        final PaymentMethod externalPaymentMethod = getExternalPaymentMethod(account.getId(), properties, callContext, context);
         if (externalPaymentMethod != null) {
             return externalPaymentMethod.getId();
         }
@@ -300,9 +314,8 @@ public class PaymentMethodProcessor extends ProcessorBase {
         return (ExternalPaymentProviderPlugin) getPaymentPluginApi(ExternalPaymentProviderPlugin.PLUGIN_NAME);
     }
 
-    private List<PaymentMethod> getPaymentMethodInternal(final List<PaymentMethodModelDao> paymentMethodModels, final boolean withPluginInfo, final Iterable<PluginProperty> properties, final TenantContext tenantContext, final InternalTenantContext context)
+    private List<PaymentMethod> getPaymentMethodInternal(final Collection<PaymentMethodModelDao> paymentMethodModels, final boolean withPluginInfo, final Iterable<PluginProperty> properties, final TenantContext tenantContext, final InternalTenantContext context)
             throws PaymentApiException {
-
         final List<PaymentMethod> result = new ArrayList<PaymentMethod>(paymentMethodModels.size());
         for (final PaymentMethodModelDao paymentMethodModel : paymentMethodModels) {
             final PaymentMethod pm = buildDefaultPaymentMethod(paymentMethodModel, withPluginInfo, properties, tenantContext, context);
@@ -351,7 +364,7 @@ public class PaymentMethodProcessor extends ProcessorBase {
                     }
                 }
             });
-        } catch (Exception e) {
+        } catch (final Exception e) {
             throw new PaymentApiException(e, ErrorCode.PAYMENT_INTERNAL_ERROR, Objects.firstNonNull(e.getMessage(), ""));
         }
     }
@@ -381,7 +394,7 @@ public class PaymentMethodProcessor extends ProcessorBase {
                     }
                 }
             });
-        } catch (Exception e) {
+        } catch (final Exception e) {
             throw new PaymentApiException(e, ErrorCode.PAYMENT_INTERNAL_ERROR, Objects.firstNonNull(e.getMessage(), ""));
         }
     }
@@ -476,7 +489,7 @@ public class PaymentMethodProcessor extends ProcessorBase {
                 }
             });
             return result.getReturnType();
-        } catch (Exception e) {
+        } catch (final Exception e) {
             throw new PaymentApiException(e, ErrorCode.PAYMENT_INTERNAL_ERROR, Objects.firstNonNull(e.getMessage(), ""));
         }
     }
diff --git a/payment/src/main/java/org/killbill/billing/payment/core/PaymentProcessor.java b/payment/src/main/java/org/killbill/billing/payment/core/PaymentProcessor.java
index f848cf0..f78753d 100644
--- a/payment/src/main/java/org/killbill/billing/payment/core/PaymentProcessor.java
+++ b/payment/src/main/java/org/killbill/billing/payment/core/PaymentProcessor.java
@@ -19,9 +19,13 @@
 package org.killbill.billing.payment.core;
 
 import java.math.BigDecimal;
+import java.util.Collection;
 import java.util.Comparator;
+import java.util.HashMap;
+import java.util.HashSet;
 import java.util.LinkedList;
 import java.util.List;
+import java.util.Map;
 import java.util.UUID;
 import java.util.concurrent.ExecutorService;
 
@@ -46,7 +50,6 @@ 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.core.sm.PaymentAutomatonRunner;
-import org.killbill.billing.payment.core.sm.PaymentStateMachineHelper;
 import org.killbill.billing.payment.dao.PaymentDao;
 import org.killbill.billing.payment.dao.PaymentModelDao;
 import org.killbill.billing.payment.dao.PaymentTransactionModelDao;
@@ -81,9 +84,10 @@ import static org.killbill.billing.util.entity.dao.DefaultPaginationHelper.getEn
 
 public class PaymentProcessor extends ProcessorBase {
 
+    private static final ImmutableList<PluginProperty> PLUGIN_PROPERTIES = ImmutableList.<PluginProperty>of();
+
     private final PaymentAutomatonRunner paymentAutomatonRunner;
     private final InternalCallContextFactory internalCallContextFactory;
-    private final PaymentStateMachineHelper paymentSMHelper;
 
     private static final Logger log = LoggerFactory.getLogger(PaymentProcessor.class);
 
@@ -98,11 +102,9 @@ public class PaymentProcessor extends ProcessorBase {
                             final GlobalLocker locker,
                             @Named(PLUGIN_EXECUTOR_NAMED) final ExecutorService executor,
                             final PaymentAutomatonRunner paymentAutomatonRunner,
-                            final PaymentStateMachineHelper paymentSMHelper,
                             final Clock clock,
                             final CacheControllerDispatcher controllerDispatcher) {
         super(pluginRegistry, accountUserApi, paymentDao, nonEntityDao, tagUserApi, locker, executor, invoiceApi, clock, controllerDispatcher);
-        this.paymentSMHelper = paymentSMHelper;
         this.internalCallContextFactory = internalCallContextFactory;
         this.paymentAutomatonRunner = paymentAutomatonRunner;
     }
@@ -116,7 +118,6 @@ public class PaymentProcessor extends ProcessorBase {
     public Payment createCapture(final boolean isApiPayment, @Nullable final UUID attemptId, final Account account, final UUID paymentId, final BigDecimal amount, final Currency currency,
                                  @Nullable final String paymentTransactionExternalKey, final boolean shouldLockAccountAndDispatch,
                                  final Iterable<PluginProperty> properties, final CallContext callContext, final InternalCallContext internalCallContext) throws PaymentApiException {
-
         return performOperation(isApiPayment, attemptId, TransactionType.CAPTURE, account, null, paymentId, null, amount, currency, null, paymentTransactionExternalKey, shouldLockAccountAndDispatch, null, properties, callContext, internalCallContext);
     }
 
@@ -145,35 +146,52 @@ public class PaymentProcessor extends ProcessorBase {
 
     public Payment createChargeback(final boolean isApiPayment, @Nullable final UUID attemptId, final Account account, final UUID paymentId, @Nullable final String paymentTransactionExternalKey, final BigDecimal amount, final Currency currency, final boolean shouldLockAccountAndDispatch,
                                     final CallContext callContext, final InternalCallContext internalCallContext) throws PaymentApiException {
-        return performOperation(isApiPayment, attemptId, TransactionType.CHARGEBACK, account, null, paymentId, null, amount, currency, null, paymentTransactionExternalKey, shouldLockAccountAndDispatch, null, ImmutableList.<PluginProperty>of(), callContext, internalCallContext);
-    }
-
-    public List<Payment> getAccountPayments(final UUID accountId, final InternalTenantContext tenantContext) throws PaymentApiException {
-        final List<PaymentModelDao> paymentsModelDao = paymentDao.getPaymentsForAccount(accountId, tenantContext);
-        final List<PaymentTransactionModelDao> transactionsModelDao = paymentDao.getTransactionsForAccount(accountId, tenantContext);
-
-        return Lists.<PaymentModelDao, Payment>transform(paymentsModelDao,
-                                                         new Function<PaymentModelDao, Payment>() {
-                                                             @Override
-                                                             public Payment apply(final PaymentModelDao curPaymentModelDao) {
-                                                                 return toPayment(curPaymentModelDao, transactionsModelDao, null);
-                                                             }
-                                                         });
+        return performOperation(isApiPayment, attemptId, TransactionType.CHARGEBACK, account, null, paymentId, null, amount, currency, null, paymentTransactionExternalKey, shouldLockAccountAndDispatch, null, PLUGIN_PROPERTIES, callContext, internalCallContext);
     }
 
     public Payment notifyPendingPaymentOfStateChanged(final Account account, final UUID transactionId, final boolean isSuccess, final CallContext callContext, final InternalCallContext internalCallContext) throws PaymentApiException {
-
         final PaymentTransactionModelDao transactionModelDao = paymentDao.getPaymentTransaction(transactionId, internalCallContext);
         if (transactionModelDao.getTransactionStatus() != TransactionStatus.PENDING) {
             throw new PaymentApiException(ErrorCode.PAYMENT_NO_SUCH_SUCCESS_PAYMENT, transactionModelDao.getPaymentId());
-
         }
 
         final OperationResult overridePluginResult = isSuccess ? OperationResult.SUCCESS : OperationResult.FAILURE;
 
         return performOperation(true, null, transactionModelDao.getTransactionType(), account, null, transactionModelDao.getPaymentId(),
                                 transactionModelDao.getId(), transactionModelDao.getAmount(), transactionModelDao.getCurrency(), null, transactionModelDao.getTransactionExternalKey(), true,
-                                overridePluginResult, ImmutableList.<PluginProperty>of(), callContext, internalCallContext);
+                                overridePluginResult, PLUGIN_PROPERTIES, callContext, internalCallContext);
+    }
+
+    public List<Payment> getAccountPayments(final UUID accountId, final boolean withPluginInfo, final TenantContext context, final InternalTenantContext tenantContext) throws PaymentApiException {
+        final List<PaymentModelDao> paymentsModelDao = paymentDao.getPaymentsForAccount(accountId, tenantContext);
+        final List<PaymentTransactionModelDao> transactionsModelDao = paymentDao.getTransactionsForAccount(accountId, tenantContext);
+
+        final Map<UUID, PaymentPluginApi> paymentPluginByPaymentMethodId = new HashMap<UUID, PaymentPluginApi>();
+        final Collection<UUID> absentPlugins = new HashSet<UUID>();
+        return Lists.<PaymentModelDao, Payment>transform(paymentsModelDao,
+                                                         new Function<PaymentModelDao, Payment>() {
+                                                             @Override
+                                                             public Payment apply(final PaymentModelDao paymentModelDao) {
+                                                                 List<PaymentTransactionInfoPlugin> pluginInfo = null;
+
+                                                                 if (withPluginInfo) {
+                                                                     PaymentPluginApi pluginApi = paymentPluginByPaymentMethodId.get(paymentModelDao.getPaymentMethodId());
+                                                                     if (pluginApi == null && !absentPlugins.contains(paymentModelDao.getPaymentMethodId())) {
+                                                                         try {
+                                                                             pluginApi = getPaymentProviderPlugin(paymentModelDao.getPaymentMethodId(), tenantContext);
+                                                                             paymentPluginByPaymentMethodId.put(paymentModelDao.getPaymentMethodId(), pluginApi);
+                                                                         } catch (final PaymentApiException e) {
+                                                                             log.warn("Unable to retrieve pluginApi for payment method " + paymentModelDao.getPaymentMethodId());
+                                                                             absentPlugins.add(paymentModelDao.getPaymentMethodId());
+                                                                         }
+                                                                     }
+
+                                                                     pluginInfo = getPaymentTransactionInfoPluginsIfNeeded(pluginApi, paymentModelDao, context);
+                                                                 }
+
+                                                                 return toPayment(paymentModelDao, transactionsModelDao, pluginInfo);
+                                                             }
+                                                         });
     }
 
     public Payment getPayment(final UUID paymentId, final boolean withPluginInfo, final Iterable<PluginProperty> properties, final TenantContext tenantContext, final InternalTenantContext internalTenantContext) throws PaymentApiException {
@@ -181,7 +199,7 @@ public class PaymentProcessor extends ProcessorBase {
         if (paymentModelDao == null) {
             return null;
         }
-        return getPayment(paymentModelDao, withPluginInfo, properties, tenantContext, internalTenantContext);
+        return toPayment(paymentModelDao, withPluginInfo, properties, tenantContext, internalTenantContext);
     }
 
     public Payment getPaymentByExternalKey(final String paymentExternalKey, final boolean withPluginInfo, final Iterable<PluginProperty> properties, final TenantContext tenantContext, final InternalTenantContext internalTenantContext) throws PaymentApiException {
@@ -189,11 +207,10 @@ public class PaymentProcessor extends ProcessorBase {
         if (paymentModelDao == null) {
             return null;
         }
-        return getPayment(paymentModelDao, withPluginInfo, properties, tenantContext, internalTenantContext);
-
+        return toPayment(paymentModelDao, withPluginInfo, properties, tenantContext, internalTenantContext);
     }
 
-    public Pagination<Payment> getPayments(final Long offset, final Long limit, final Iterable<PluginProperty> properties,
+    public Pagination<Payment> getPayments(final Long offset, final Long limit, final boolean withPluginInfo, final Iterable<PluginProperty> properties,
                                            final TenantContext tenantContext, final InternalTenantContext internalTenantContext) {
         return getEntityPaginationFromPlugins(getAvailablePlugins(),
                                               offset,
@@ -201,109 +218,102 @@ public class PaymentProcessor extends ProcessorBase {
                                               new EntityPaginationBuilder<Payment, PaymentApiException>() {
                                                   @Override
                                                   public Pagination<Payment> build(final Long offset, final Long limit, final String pluginName) throws PaymentApiException {
-                                                      final Pagination<Payment> result = getPayments(offset, limit, pluginName, properties, tenantContext, internalTenantContext);
-                                                      return result;
+                                                      return getPayments(offset, limit, pluginName, withPluginInfo, properties, tenantContext, internalTenantContext);
                                                   }
                                               }
                                              );
     }
 
-    public Pagination<Payment> getPayments(final Long offset, final Long limit, final String pluginName, final Iterable<PluginProperty> properties, final TenantContext tenantContext, final InternalTenantContext internalTenantContext) throws PaymentApiException {
-        final PaymentPluginApi pluginApi = getPaymentPluginApi(pluginName);
+    public Pagination<Payment> getPayments(final Long offset, final Long limit, final String pluginName, final boolean withPluginInfo, final Iterable<PluginProperty> properties, final TenantContext tenantContext, final InternalTenantContext internalTenantContext) throws PaymentApiException {
+        final PaymentPluginApi pluginApi = withPluginInfo ? getPaymentPluginApi(pluginName) : null;
 
         return getEntityPagination(limit,
                                    new SourcePaginationBuilder<PaymentModelDao, PaymentApiException>() {
                                        @Override
                                        public Pagination<PaymentModelDao> build() {
                                            // Find all payments for all accounts
-                                           final Pagination<PaymentModelDao> result = paymentDao.getPayments(pluginName, offset, limit, internalTenantContext);
-                                           return result;
+                                           return paymentDao.getPayments(pluginName, offset, limit, internalTenantContext);
                                        }
                                    },
                                    new Function<PaymentModelDao, Payment>() {
                                        @Override
                                        public Payment apply(final PaymentModelDao paymentModelDao) {
-                                           List<PaymentTransactionInfoPlugin> pluginInfo = null;
-                                           try {
-                                               pluginInfo = pluginApi.getPaymentInfo(paymentModelDao.getAccountId(), paymentModelDao.getId(), properties, tenantContext);
-                                           } catch (final PaymentPluginApiException e) {
-                                               log.warn("Unable to find payment id " + paymentModelDao.getId() + " in plugin " + pluginName);
-                                               // We still want to return a payment object, even though the plugin details are missing
-                                           }
-
+                                           final List<PaymentTransactionInfoPlugin> pluginInfo = getPaymentTransactionInfoPluginsIfNeeded(pluginApi, paymentModelDao, tenantContext);
                                            return toPayment(paymentModelDao.getId(), pluginInfo, internalTenantContext);
                                        }
                                    }
                                   );
     }
 
-    public Pagination<Payment> searchPayments(final String searchKey, final Long offset, final Long limit, final Iterable<PluginProperty> properties, final TenantContext tenantContext, final InternalTenantContext internalTenantContext) {
+    public Pagination<Payment> searchPayments(final String searchKey, final Long offset, final Long limit, final boolean withPluginInfo, final Iterable<PluginProperty> properties, final TenantContext tenantContext, final InternalTenantContext internalTenantContext) {
         return getEntityPaginationFromPlugins(getAvailablePlugins(),
                                               offset,
                                               limit,
                                               new EntityPaginationBuilder<Payment, PaymentApiException>() {
                                                   @Override
                                                   public Pagination<Payment> build(final Long offset, final Long limit, final String pluginName) throws PaymentApiException {
-                                                      return searchPayments(searchKey, offset, limit, pluginName, properties, tenantContext, internalTenantContext);
+                                                      return searchPayments(searchKey, offset, limit, pluginName, withPluginInfo, properties, tenantContext, internalTenantContext);
                                                   }
                                               }
                                              );
     }
 
-    public Pagination<Payment> searchPayments(final String searchKey, final Long offset, final Long limit, final String pluginName, final Iterable<PluginProperty> properties, final TenantContext tenantContext, final InternalTenantContext internalTenantContext) throws PaymentApiException {
-        final PaymentPluginApi pluginApi = getPaymentPluginApi(pluginName);
+    public Pagination<Payment> searchPayments(final String searchKey, final Long offset, final Long limit, final String pluginName, final boolean withPluginInfo, final Iterable<PluginProperty> properties, final TenantContext tenantContext, final InternalTenantContext internalTenantContext) throws PaymentApiException {
+        if (withPluginInfo) {
+            final PaymentPluginApi pluginApi = getPaymentPluginApi(pluginName);
+
+            return getEntityPagination(limit,
+                                       new SourcePaginationBuilder<PaymentTransactionInfoPlugin, PaymentApiException>() {
+                                           @Override
+                                           public Pagination<PaymentTransactionInfoPlugin> build() throws PaymentApiException {
+                                               try {
+                                                   return pluginApi.searchPayments(searchKey, offset, limit, properties, tenantContext);
+                                               } catch (final PaymentPluginApiException e) {
+                                                   throw new PaymentApiException(e, ErrorCode.PAYMENT_PLUGIN_SEARCH_PAYMENTS, pluginName, searchKey);
+                                               }
+                                           }
 
-        return getEntityPagination(limit,
-                                   new SourcePaginationBuilder<PaymentTransactionInfoPlugin, PaymentApiException>() {
-                                       @Override
-                                       public Pagination<PaymentTransactionInfoPlugin> build() throws PaymentApiException {
-                                           try {
-                                               return pluginApi.searchPayments(searchKey, offset, limit, properties, tenantContext);
-                                           } catch (final PaymentPluginApiException e) {
-                                               throw new PaymentApiException(e, ErrorCode.PAYMENT_PLUGIN_SEARCH_PAYMENTS, pluginName, searchKey);
+                                       },
+                                       new Function<PaymentTransactionInfoPlugin, Payment>() {
+                                           final List<PaymentTransactionInfoPlugin> cachedPaymentTransactions = new LinkedList<PaymentTransactionInfoPlugin>();
+
+                                           @Override
+                                           public Payment apply(final PaymentTransactionInfoPlugin pluginTransaction) {
+                                               if (pluginTransaction.getKbPaymentId() == null) {
+                                                   // Garbage from the plugin?
+                                                   log.debug("Plugin {} returned a payment without a kbPaymentId for searchKey {}", pluginName, searchKey);
+                                                   return null;
+                                               }
+
+                                               if (cachedPaymentTransactions.isEmpty() ||
+                                                   (cachedPaymentTransactions.get(0).getKbPaymentId().equals(pluginTransaction.getKbPaymentId()))) {
+                                                   cachedPaymentTransactions.add(pluginTransaction);
+                                                   return null;
+                                               } else {
+                                                   final Payment result = toPayment(pluginTransaction.getKbPaymentId(), ImmutableList.<PaymentTransactionInfoPlugin>copyOf(cachedPaymentTransactions), internalTenantContext);
+                                                   cachedPaymentTransactions.clear();
+                                                   cachedPaymentTransactions.add(pluginTransaction);
+                                                   return result;
+                                               }
                                            }
                                        }
-
-                                   },
-                                   new Function<PaymentTransactionInfoPlugin, Payment>() {
-
-                                       final List<PaymentTransactionInfoPlugin> cachedPaymentTransactions = new LinkedList<PaymentTransactionInfoPlugin>();
-
-                                       @Override
-                                       public Payment apply(final PaymentTransactionInfoPlugin pluginTransaction) {
-
-                                           if (pluginTransaction.getKbPaymentId() == null) {
-                                               // Garbage from the plugin?
-                                               log.debug("Plugin {} returned a payment without a kbPaymentId for searchKey {}", pluginName, searchKey);
-                                               return null;
+                                      );
+        } else {
+            return getEntityPagination(limit,
+                                       new SourcePaginationBuilder<PaymentModelDao, PaymentApiException>() {
+                                           @Override
+                                           public Pagination<PaymentModelDao> build() {
+                                               return paymentDao.searchPayments(searchKey, offset, limit, internalTenantContext);
                                            }
-
-                                           if (cachedPaymentTransactions.isEmpty() ||
-                                               (cachedPaymentTransactions.get(0).getKbPaymentId().equals(pluginTransaction.getKbPaymentId()))) {
-                                               cachedPaymentTransactions.add(pluginTransaction);
-                                               return null;
-                                           } else {
-                                               final Payment result = toPayment(pluginTransaction.getKbPaymentId(), ImmutableList.<PaymentTransactionInfoPlugin>copyOf(cachedPaymentTransactions), internalTenantContext);
-                                               cachedPaymentTransactions.clear();
-                                               cachedPaymentTransactions.add(pluginTransaction);
-                                               return result;
+                                       },
+                                       new Function<PaymentModelDao, Payment>() {
+                                           @Override
+                                           public Payment apply(final PaymentModelDao paymentModelDao) {
+                                               return toPayment(paymentModelDao.getId(), null, internalTenantContext);
                                            }
                                        }
-                                   }
-                                  );
-    }
-
-    public Payment toPayment(final UUID paymentId, @Nullable final List<PaymentTransactionInfoPlugin> pluginTransactions, final InternalTenantContext tenantContext) {
-        final PaymentModelDao paymentModelDao = paymentDao.getPayment(paymentId, tenantContext);
-        if (paymentModelDao == null) {
-            log.warn("Unable to find payment id " + paymentId);
-            return null;
+                                      );
         }
-
-        final InternalTenantContext tenantContextWithAccountRecordId = internalCallContextFactory.createInternalTenantContext(paymentModelDao.getAccountId(), tenantContext);
-        final List<PaymentTransactionModelDao> transactionsForAccount = paymentDao.getTransactionsForAccount(paymentModelDao.getAccountId(), tenantContextWithAccountRecordId);
-
-        return toPayment(paymentModelDao, transactionsForAccount, pluginTransactions);
     }
 
     private Payment performOperation(final boolean isApiPayment, @Nullable final UUID attemptId,
@@ -314,7 +324,6 @@ public class PaymentProcessor extends ProcessorBase {
                                      final boolean shouldLockAccountAndDispatch, @Nullable final OperationResult overridePluginOperationResult,
                                      final Iterable<PluginProperty> properties,
                                      final CallContext callContext, final InternalCallContext internalCallContext) throws PaymentApiException {
-
         validateUniqueTransactionExternalKey(paymentTransactionExternalKey, internalCallContext);
         final UUID nonNullPaymentId = paymentAutomatonRunner.run(isApiPayment,
                                                                  transactionType,
@@ -335,27 +344,55 @@ public class PaymentProcessor extends ProcessorBase {
         return getPayment(nonNullPaymentId, true, properties, callContext, internalCallContext);
     }
 
-    private Payment getPayment(final PaymentModelDao paymentModelDao, final boolean withPluginInfo, final Iterable<PluginProperty> properties, final TenantContext context, final InternalTenantContext tenantContext) throws PaymentApiException {
-        final InternalTenantContext tenantContextWithAccountRecordId;
-        if (tenantContext.getAccountRecordId() == null) {
-            tenantContextWithAccountRecordId = internalCallContextFactory.createInternalTenantContext(paymentModelDao.getAccountId(), tenantContext);
-        } else {
-            tenantContextWithAccountRecordId = tenantContext;
+    // Used in bulk get API (getAccountPayments / getPayments)
+    private List<PaymentTransactionInfoPlugin> getPaymentTransactionInfoPluginsIfNeeded(@Nullable final PaymentPluginApi pluginApi, final PaymentModelDao paymentModelDao, final TenantContext context) {
+        if (pluginApi == null) {
+            return null;
         }
-        final List<PaymentTransactionModelDao> transactionsForPayment = paymentDao.getTransactionsForPayment(paymentModelDao.getId(), tenantContextWithAccountRecordId);
 
-        final PaymentPluginApi plugin = withPluginInfo ? getPaymentProviderPlugin(paymentModelDao.getPaymentMethodId(), tenantContext) : null;
-        List<PaymentTransactionInfoPlugin> pluginInfo = null;
-        if (plugin != null) {
-            try {
-                pluginInfo = plugin.getPaymentInfo(paymentModelDao.getAccountId(), paymentModelDao.getId(), properties, context);
-            } catch (final PaymentPluginApiException e) {
-                throw new PaymentApiException(ErrorCode.PAYMENT_PLUGIN_GET_PAYMENT_INFO, paymentModelDao.getId(), e.toString());
-            }
+        try {
+            return getPaymentTransactionInfoPlugins(pluginApi, paymentModelDao, PLUGIN_PROPERTIES, context);
+        } catch (final PaymentApiException e) {
+            log.warn("Unable to retrieve plugin info for payment " + paymentModelDao.getId());
+            return null;
         }
-        return toPayment(paymentModelDao, transactionsForPayment, pluginInfo);
     }
 
+    private List<PaymentTransactionInfoPlugin> getPaymentTransactionInfoPlugins(final PaymentPluginApi plugin, final PaymentModelDao paymentModelDao, final Iterable<PluginProperty> properties, final TenantContext context) throws PaymentApiException {
+        try {
+            return plugin.getPaymentInfo(paymentModelDao.getAccountId(), paymentModelDao.getId(), properties, context);
+        } catch (final PaymentPluginApiException e) {
+            throw new PaymentApiException(ErrorCode.PAYMENT_PLUGIN_GET_PAYMENT_INFO, paymentModelDao.getId(), e.toString());
+        }
+    }
+
+    // Used in bulk get APIs (getPayments / searchPayments)
+    private Payment toPayment(final UUID paymentId, @Nullable final Iterable<PaymentTransactionInfoPlugin> pluginTransactions, final InternalTenantContext tenantContext) {
+        final PaymentModelDao paymentModelDao = paymentDao.getPayment(paymentId, tenantContext);
+        if (paymentModelDao == null) {
+            log.warn("Unable to find payment id " + paymentId);
+            return null;
+        }
+
+        return toPayment(paymentModelDao, pluginTransactions, tenantContext);
+    }
+
+    // Used in single get APIs (getPayment / getPaymentByExternalKey)
+    private Payment toPayment(final PaymentModelDao paymentModelDao, final boolean withPluginInfo, final Iterable<PluginProperty> properties, final TenantContext context, final InternalTenantContext tenantContext) throws PaymentApiException {
+        final PaymentPluginApi plugin = getPaymentProviderPlugin(paymentModelDao.getPaymentMethodId(), tenantContext);
+        final List<PaymentTransactionInfoPlugin> pluginTransactions = withPluginInfo ? getPaymentTransactionInfoPlugins(plugin, paymentModelDao, properties, context) : null;
+
+        return toPayment(paymentModelDao, pluginTransactions, tenantContext);
+    }
+
+    private Payment toPayment(final PaymentModelDao paymentModelDao, @Nullable final Iterable<PaymentTransactionInfoPlugin> pluginTransactions, final InternalTenantContext tenantContext) {
+        final InternalTenantContext tenantContextWithAccountRecordId = getInternalTenantContextWithAccountRecordId(paymentModelDao.getAccountId(), tenantContext);
+        final List<PaymentTransactionModelDao> transactionsForPayment = paymentDao.getTransactionsForPayment(paymentModelDao.getId(), tenantContextWithAccountRecordId);
+
+        return toPayment(paymentModelDao, transactionsForPayment, pluginTransactions);
+    }
+
+    // Used in bulk get API (getAccountPayments)
     private Payment toPayment(final PaymentModelDao curPaymentModelDao, final Iterable<PaymentTransactionModelDao> transactionsModelDao, @Nullable final Iterable<PaymentTransactionInfoPlugin> pluginTransactions) {
         final Ordering<PaymentTransaction> perPaymentTransactionOrdering = Ordering.<PaymentTransaction>from(new Comparator<PaymentTransaction>() {
             @Override
@@ -364,6 +401,7 @@ public class PaymentProcessor extends ProcessorBase {
             }
         });
 
+        // Need to filter for optimized codepaths looking up by account_record_id
         final Iterable<PaymentTransactionModelDao> filteredTransactions = Iterables.filter(transactionsModelDao, new Predicate<PaymentTransactionModelDao>() {
             @Override
             public boolean apply(final PaymentTransactionModelDao curPaymentTransactionModelDao) {
@@ -393,4 +431,14 @@ public class PaymentProcessor extends ProcessorBase {
         return new DefaultPayment(curPaymentModelDao.getId(), curPaymentModelDao.getCreatedDate(), curPaymentModelDao.getUpdatedDate(), curPaymentModelDao.getAccountId(),
                                   curPaymentModelDao.getPaymentMethodId(), curPaymentModelDao.getPaymentNumber(), curPaymentModelDao.getExternalKey(), sortedTransactions);
     }
+
+    private InternalTenantContext getInternalTenantContextWithAccountRecordId(final UUID accountId, final InternalTenantContext tenantContext) {
+        final InternalTenantContext tenantContextWithAccountRecordId;
+        if (tenantContext.getAccountRecordId() == null) {
+            tenantContextWithAccountRecordId = internalCallContextFactory.createInternalTenantContext(accountId, tenantContext);
+        } else {
+            tenantContextWithAccountRecordId = tenantContext;
+        }
+        return tenantContextWithAccountRecordId;
+    }
 }
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 afb88fa..6423866 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
@@ -214,6 +214,25 @@ public class DefaultPaymentDao implements PaymentDao {
     }
 
     @Override
+    public Pagination<PaymentModelDao> searchPayments(final String searchKey, final Long offset, final Long limit, final InternalTenantContext context) {
+        return paginationHelper.getPagination(PaymentSqlDao.class,
+                                              new PaginationIteratorBuilder<PaymentModelDao, Payment, PaymentSqlDao>() {
+                                                  @Override
+                                                  public Long getCount(final PaymentSqlDao paymentSqlDao, final InternalTenantContext context) {
+                                                      return paymentSqlDao.getSearchCount(searchKey, String.format("%%%s%%", searchKey), context);
+                                                  }
+
+                                                  @Override
+                                                  public Iterator<PaymentModelDao> build(final PaymentSqlDao paymentSqlDao, final Long limit, final InternalTenantContext context) {
+                                                      return paymentSqlDao.search(searchKey, String.format("%%%s%%", searchKey), offset, limit, context);
+                                                  }
+                                              },
+                                              offset,
+                                              limit,
+                                              context);
+    }
+
+    @Override
     public PaymentModelDao insertPaymentWithFirstTransaction(final PaymentModelDao payment, final PaymentTransactionModelDao paymentTransaction, final InternalCallContext context) {
 
         return transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<PaymentModelDao>() {
@@ -271,7 +290,6 @@ public class DefaultPaymentDao implements PaymentDao {
 
     }
 
-
     @Override
     public PaymentModelDao getPayment(final UUID paymentId, final InternalTenantContext context) {
         return transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<PaymentModelDao>() {
@@ -393,6 +411,25 @@ public class DefaultPaymentDao implements PaymentDao {
     }
 
     @Override
+    public Pagination<PaymentMethodModelDao> searchPaymentMethods(final String searchKey, final Long offset, final Long limit, final InternalTenantContext context) {
+        return paginationHelper.getPagination(PaymentMethodSqlDao.class,
+                                              new PaginationIteratorBuilder<PaymentMethodModelDao, PaymentMethod, PaymentMethodSqlDao>() {
+                                                  @Override
+                                                  public Long getCount(final PaymentMethodSqlDao paymentMethodSqlDao, final InternalTenantContext context) {
+                                                      return paymentMethodSqlDao.getSearchCount(searchKey, String.format("%%%s%%", searchKey), context);
+                                                  }
+
+                                                  @Override
+                                                  public Iterator<PaymentMethodModelDao> build(final PaymentMethodSqlDao paymentMethodSqlDao, final Long limit, final InternalTenantContext context) {
+                                                      return paymentMethodSqlDao.search(searchKey, String.format("%%%s%%", searchKey), offset, limit, context);
+                                                  }
+                                              },
+                                              offset,
+                                              limit,
+                                              context);
+    }
+
+    @Override
     public Pagination<PaymentMethodModelDao> getPaymentMethods(final String pluginName, final Long offset, final Long limit, final InternalTenantContext context) {
         return paginationHelper.getPagination(PaymentMethodSqlDao.class,
                                               new PaginationIteratorBuilder<PaymentMethodModelDao, PaymentMethod, PaymentMethodSqlDao>() {
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 efdea6c..8055938 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
@@ -48,14 +48,15 @@ public interface PaymentDao {
 
     public Pagination<PaymentModelDao> getPayments(String pluginName, Long offset, Long limit, InternalTenantContext context);
 
+    public Pagination<PaymentModelDao> searchPayments(String searchKey, Long offset, Long limit, InternalTenantContext context);
+
     public PaymentModelDao insertPaymentWithFirstTransaction(PaymentModelDao payment, PaymentTransactionModelDao paymentTransaction, InternalCallContext context);
 
     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,
-                                                              TransactionStatus paymentStatus, BigDecimal processedAmount, Currency processedCurrency,
-                                                              String gatewayErrorCode, String gatewayErrorMsg, InternalCallContext context);
-
+                                                        TransactionStatus paymentStatus, BigDecimal processedAmount, Currency processedCurrency,
+                                                        String gatewayErrorCode, String gatewayErrorMsg, InternalCallContext context);
 
     public PaymentModelDao getPayment(UUID paymentId, InternalTenantContext context);
 
@@ -83,6 +84,8 @@ public interface PaymentDao {
 
     public Pagination<PaymentMethodModelDao> getPaymentMethods(String pluginName, Long offset, Long limit, InternalTenantContext context);
 
+    public Pagination<PaymentMethodModelDao> searchPaymentMethods(String searchKey, Long offset, Long limit, InternalTenantContext context);
+
     public void deletedPaymentMethod(UUID paymentMethodId, InternalCallContext context);
 
     public List<PaymentMethodModelDao> refreshPaymentMethods(UUID accountId, String pluginName, List<PaymentMethodModelDao> paymentMethods, InternalCallContext context);
diff --git a/payment/src/main/resources/org/killbill/billing/payment/dao/PaymentMethodSqlDao.sql.stg b/payment/src/main/resources/org/killbill/billing/payment/dao/PaymentMethodSqlDao.sql.stg
index 56d48c9..aee2984 100644
--- a/payment/src/main/resources/org/killbill/billing/payment/dao/PaymentMethodSqlDao.sql.stg
+++ b/payment/src/main/resources/org/killbill/billing/payment/dao/PaymentMethodSqlDao.sql.stg
@@ -30,7 +30,6 @@ tableValues() ::= <<
 , :updatedDate
 >>
 
-
 markPaymentMethodAsDeleted(id) ::= <<
 update <tableName()>
 set is_active = 0
@@ -92,6 +91,13 @@ where account_id = :accountId
 ;
 >>
 
+searchQuery(prefix) ::= <<
+     <idField(prefix)> = :searchKey
+  or <prefix>external_key like :likeSearchKey
+  or <prefix>account_id = :searchKey
+  or <prefix>plugin_name like :likeSearchKey
+>>
+
 getByPluginName() ::= <<
 select
 <allTableFields("t.")>
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 1cb5262..51ca239 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
@@ -74,6 +74,13 @@ where external_key = :externalKey
 ;
 >>
 
+searchQuery(prefix) ::= <<
+     <idField(prefix)> = :searchKey
+  or <prefix>account_id = :searchKey
+  or <prefix>payment_method_id = :searchKey
+  or <prefix>external_key like :likeSearchKey
+  or <prefix>state_name like :likeSearchKey
+>>
 
 getByPluginName() ::= <<
 select
diff --git a/payment/src/test/java/org/killbill/billing/payment/core/TestPaymentProcessor.java b/payment/src/test/java/org/killbill/billing/payment/core/TestPaymentProcessor.java
index 72541ec..caa98f1 100644
--- a/payment/src/test/java/org/killbill/billing/payment/core/TestPaymentProcessor.java
+++ b/payment/src/test/java/org/killbill/billing/payment/core/TestPaymentProcessor.java
@@ -76,7 +76,7 @@ public class TestPaymentProcessor extends PaymentTestSuiteWithEmbeddedDB {
         // AUTH pre-3DS
         final String authorizationKey = UUID.randomUUID().toString();
         final Payment authorization = paymentProcessor.createAuthorization(true, null, account, null, null, TEN, CURRENCY, paymentExternalKey, authorizationKey,
-                                                                                       SHOULD_LOCK_ACCOUNT, PLUGIN_PROPERTIES, callContext, internalCallContext);
+                                                                           SHOULD_LOCK_ACCOUNT, PLUGIN_PROPERTIES, callContext, internalCallContext);
         verifyPayment(authorization, paymentExternalKey, TEN, ZERO, ZERO, 1);
         final UUID paymentId = authorization.getId();
         verifyPaymentTransaction(authorization.getTransactions().get(0), authorizationKey, TransactionType.AUTHORIZE, TEN, paymentId);
@@ -85,7 +85,7 @@ public class TestPaymentProcessor extends PaymentTestSuiteWithEmbeddedDB {
         // AUTH post-3DS
         final String authorizationPost3DSKey = UUID.randomUUID().toString();
         final Payment authorizationPost3DS = paymentProcessor.createAuthorization(true, null, account, null, paymentId, TEN, CURRENCY, paymentExternalKey, authorizationPost3DSKey,
-                                                                                              SHOULD_LOCK_ACCOUNT, PLUGIN_PROPERTIES, callContext, internalCallContext);
+                                                                                  SHOULD_LOCK_ACCOUNT, PLUGIN_PROPERTIES, callContext, internalCallContext);
         verifyPayment(authorizationPost3DS, paymentExternalKey, TEN, ZERO, ZERO, 2);
         verifyPaymentTransaction(authorizationPost3DS.getTransactions().get(1), authorizationPost3DSKey, TransactionType.AUTHORIZE, TEN, paymentId);
         paymentBusListener.verify(2, account.getId(), paymentId, TEN);
@@ -93,7 +93,7 @@ public class TestPaymentProcessor extends PaymentTestSuiteWithEmbeddedDB {
         // CAPTURE
         final String capture1Key = UUID.randomUUID().toString();
         final Payment partialCapture1 = paymentProcessor.createCapture(true, null, account, paymentId, FIVE, CURRENCY, capture1Key,
-                                                                                   SHOULD_LOCK_ACCOUNT, PLUGIN_PROPERTIES, callContext, internalCallContext);
+                                                                       SHOULD_LOCK_ACCOUNT, PLUGIN_PROPERTIES, callContext, internalCallContext);
         verifyPayment(partialCapture1, paymentExternalKey, TEN, FIVE, ZERO, 3);
         verifyPaymentTransaction(partialCapture1.getTransactions().get(2), capture1Key, TransactionType.CAPTURE, FIVE, paymentId);
         paymentBusListener.verify(3, account.getId(), paymentId, FIVE);
@@ -101,7 +101,7 @@ public class TestPaymentProcessor extends PaymentTestSuiteWithEmbeddedDB {
         // CAPTURE
         final String capture2Key = UUID.randomUUID().toString();
         final Payment partialCapture2 = paymentProcessor.createCapture(true, null, account, paymentId, FIVE, CURRENCY, capture2Key,
-                                                                                   SHOULD_LOCK_ACCOUNT, PLUGIN_PROPERTIES, callContext, internalCallContext);
+                                                                       SHOULD_LOCK_ACCOUNT, PLUGIN_PROPERTIES, callContext, internalCallContext);
         verifyPayment(partialCapture2, paymentExternalKey, TEN, TEN, ZERO, 4);
         verifyPaymentTransaction(partialCapture2.getTransactions().get(3), capture2Key, TransactionType.CAPTURE, FIVE, paymentId);
         paymentBusListener.verify(4, account.getId(), paymentId, FIVE);
@@ -109,7 +109,7 @@ public class TestPaymentProcessor extends PaymentTestSuiteWithEmbeddedDB {
         // REFUND
         final String refund1Key = UUID.randomUUID().toString();
         final Payment partialRefund1 = paymentProcessor.createRefund(true, null, account, paymentId, FIVE, CURRENCY, refund1Key,
-                                                                                 SHOULD_LOCK_ACCOUNT, PLUGIN_PROPERTIES, callContext, internalCallContext);
+                                                                     SHOULD_LOCK_ACCOUNT, PLUGIN_PROPERTIES, callContext, internalCallContext);
         verifyPayment(partialRefund1, paymentExternalKey, TEN, TEN, FIVE, 5);
         verifyPaymentTransaction(partialRefund1.getTransactions().get(4), refund1Key, TransactionType.REFUND, FIVE, paymentId);
         paymentBusListener.verify(5, account.getId(), paymentId, FIVE);
@@ -117,7 +117,7 @@ public class TestPaymentProcessor extends PaymentTestSuiteWithEmbeddedDB {
         // REFUND
         final String refund2Key = UUID.randomUUID().toString();
         final Payment partialRefund2 = paymentProcessor.createRefund(true, null, account, paymentId, FIVE, CURRENCY, refund2Key,
-                                                                                 SHOULD_LOCK_ACCOUNT, PLUGIN_PROPERTIES, callContext, internalCallContext);
+                                                                     SHOULD_LOCK_ACCOUNT, PLUGIN_PROPERTIES, callContext, internalCallContext);
         verifyPayment(partialRefund2, paymentExternalKey, TEN, TEN, TEN, 6);
         verifyPaymentTransaction(partialRefund2.getTransactions().get(5), refund2Key, TransactionType.REFUND, FIVE, paymentId);
         paymentBusListener.verify(6, account.getId(), paymentId, FIVE);
@@ -130,7 +130,7 @@ public class TestPaymentProcessor extends PaymentTestSuiteWithEmbeddedDB {
         // AUTH
         final String authorizationKey = UUID.randomUUID().toString();
         final Payment authorization = paymentProcessor.createAuthorization(true, null, account, null, null, TEN, CURRENCY, paymentExternalKey, authorizationKey,
-                                                                                       SHOULD_LOCK_ACCOUNT, PLUGIN_PROPERTIES, callContext, internalCallContext);
+                                                                           SHOULD_LOCK_ACCOUNT, PLUGIN_PROPERTIES, callContext, internalCallContext);
         verifyPayment(authorization, paymentExternalKey, TEN, ZERO, ZERO, 1);
         final UUID paymentId = authorization.getId();
         verifyPaymentTransaction(authorization.getTransactions().get(0), authorizationKey, TransactionType.AUTHORIZE, TEN, paymentId);
@@ -139,7 +139,7 @@ public class TestPaymentProcessor extends PaymentTestSuiteWithEmbeddedDB {
         // VOID
         final String voidKey = UUID.randomUUID().toString();
         final Payment voidTransaction = paymentProcessor.createVoid(true, null, account, paymentId, voidKey,
-                                                                                SHOULD_LOCK_ACCOUNT, PLUGIN_PROPERTIES, callContext, internalCallContext);
+                                                                    SHOULD_LOCK_ACCOUNT, PLUGIN_PROPERTIES, callContext, internalCallContext);
         verifyPayment(voidTransaction, paymentExternalKey, TEN, ZERO, ZERO, 2);
         verifyPaymentTransaction(voidTransaction.getTransactions().get(1), voidKey, TransactionType.VOID, null, paymentId);
         paymentBusListener.verify(2, account.getId(), paymentId, null);
@@ -152,7 +152,7 @@ public class TestPaymentProcessor extends PaymentTestSuiteWithEmbeddedDB {
         // PURCHASE
         final String purchaseKey = UUID.randomUUID().toString();
         final Payment purchase = paymentProcessor.createPurchase(true, null, account, null, null, TEN, CURRENCY, paymentExternalKey, purchaseKey,
-                                                                             SHOULD_LOCK_ACCOUNT, PLUGIN_PROPERTIES, callContext, internalCallContext);
+                                                                 SHOULD_LOCK_ACCOUNT, PLUGIN_PROPERTIES, callContext, internalCallContext);
         verifyPayment(purchase, paymentExternalKey, ZERO, ZERO, ZERO, 1);
         final UUID paymentId = purchase.getId();
         verifyPaymentTransaction(purchase.getTransactions().get(0), purchaseKey, TransactionType.PURCHASE, TEN, paymentId);
@@ -166,7 +166,7 @@ public class TestPaymentProcessor extends PaymentTestSuiteWithEmbeddedDB {
         // CREDIT
         final String creditKey = UUID.randomUUID().toString();
         final Payment purchase = paymentProcessor.createCredit(true, null, account, null, null, TEN, CURRENCY, paymentExternalKey, creditKey,
-                                                                           SHOULD_LOCK_ACCOUNT, PLUGIN_PROPERTIES, callContext, internalCallContext);
+                                                               SHOULD_LOCK_ACCOUNT, PLUGIN_PROPERTIES, callContext, internalCallContext);
         verifyPayment(purchase, paymentExternalKey, ZERO, ZERO, ZERO, 1);
         final UUID paymentId = purchase.getId();
         verifyPaymentTransaction(purchase.getTransactions().get(0), creditKey, TransactionType.CREDIT, TEN, paymentId);
@@ -174,8 +174,8 @@ public class TestPaymentProcessor extends PaymentTestSuiteWithEmbeddedDB {
     }
 
     private void verifyPayment(final Payment payment, final String paymentExternalKey,
-                                     final BigDecimal authAmount, final BigDecimal capturedAmount, final BigDecimal refundedAmount,
-                                     final int transactionsSize) {
+                               final BigDecimal authAmount, final BigDecimal capturedAmount, final BigDecimal refundedAmount,
+                               final int transactionsSize) {
         Assert.assertEquals(payment.getAccountId(), account.getId());
         // We cannot assume the number to be 1 here as the auto_increment implementation
         // depends on the database. On h2, it is implemented as a sequence, and the payment number
@@ -191,7 +191,7 @@ public class TestPaymentProcessor extends PaymentTestSuiteWithEmbeddedDB {
     }
 
     private void verifyPaymentTransaction(final PaymentTransaction paymentTransaction, final String paymentTransactionExternalKey,
-                                                final TransactionType transactionType, @Nullable final BigDecimal amount, final UUID paymentId) {
+                                          final TransactionType transactionType, @Nullable final BigDecimal amount, final UUID paymentId) {
         Assert.assertEquals(paymentTransaction.getPaymentId(), paymentId);
         Assert.assertEquals(paymentTransaction.getExternalKey(), paymentTransactionExternalKey);
         Assert.assertEquals(paymentTransaction.getTransactionType(), transactionType);
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 f36e9bb..2207741 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
@@ -63,7 +63,6 @@ public class MockPaymentDao implements PaymentDao {
         return result;
     }
 
-
     @Override
     public PaymentAttemptModelDao insertPaymentAttemptWithProperties(final PaymentAttemptModelDao attempt, final InternalCallContext context) {
         synchronized (this) {
@@ -152,6 +151,11 @@ public class MockPaymentDao implements PaymentDao {
     }
 
     @Override
+    public Pagination<PaymentModelDao> searchPayments(final String searchKey, final Long offset, final Long limit, final InternalTenantContext context) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
     public PaymentModelDao insertPaymentWithFirstTransaction(final PaymentModelDao payment, final PaymentTransactionModelDao paymentTransaction, final InternalCallContext context) {
         synchronized (this) {
             payments.put(payment.getId(), payment);
@@ -309,6 +313,11 @@ public class MockPaymentDao implements PaymentDao {
     }
 
     @Override
+    public Pagination<PaymentMethodModelDao> searchPaymentMethods(final String searchKey, final Long offset, final Long limit, final InternalTenantContext context) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
     public void deletedPaymentMethod(final UUID paymentMethodId, final InternalCallContext context) {
         synchronized (this) {
             final Iterator<PaymentMethodModelDao> it = paymentMethods.iterator();
diff --git a/payment/src/test/java/org/killbill/billing/payment/dao/TestDefaultPaymentDao.java b/payment/src/test/java/org/killbill/billing/payment/dao/TestDefaultPaymentDao.java
index e92f546..13e8891 100644
--- a/payment/src/test/java/org/killbill/billing/payment/dao/TestDefaultPaymentDao.java
+++ b/payment/src/test/java/org/killbill/billing/payment/dao/TestDefaultPaymentDao.java
@@ -29,6 +29,8 @@ import org.killbill.billing.payment.api.TransactionType;
 import org.testng.Assert;
 import org.testng.annotations.Test;
 
+import com.google.common.collect.ImmutableList;
+
 public class TestDefaultPaymentDao extends PaymentTestSuiteWithEmbeddedDB {
 
     @Test(groups = "slow")
@@ -86,8 +88,15 @@ public class TestDefaultPaymentDao extends PaymentTestSuiteWithEmbeddedDB {
 
             final PaymentModelDao insertedPaymentModelDao = paymentDao.insertPaymentWithFirstTransaction(paymentModelDao, paymentTransactionModelDao, accountCallContext);
             verifyPayment(insertedPaymentModelDao, paymentModelDao);
+
+            // Verify search APIs
+            Assert.assertEquals(ImmutableList.<PaymentModelDao>copyOf(paymentDao.searchPayments(paymentModelDao.getPaymentMethodId().toString(), 0L, 100L, internalCallContext).iterator()).size(), 1);
+            Assert.assertEquals(ImmutableList.<PaymentModelDao>copyOf(paymentDao.searchPayments(paymentModelDao.getExternalKey(), 0L, 100L, internalCallContext).iterator()).size(), 1);
         }
         Assert.assertEquals(paymentDao.getPaymentsForAccount(specifiedFirstPaymentModelDao.getAccountId(), accountCallContext).size(), 4);
+
+        // Verify search APIs
+        Assert.assertEquals(ImmutableList.<PaymentModelDao>copyOf(paymentDao.searchPayments(accountId.toString(), 0L, 100L, internalCallContext).iterator()).size(), 4);
     }
 
     private void verifyPaymentAndTransactions(final InternalCallContext accountCallContext, final PaymentModelDao specifiedFirstPaymentModelDao, final PaymentTransactionModelDao... specifiedFirstPaymentTransactionModelDaos) {
@@ -153,6 +162,10 @@ public class TestDefaultPaymentDao extends PaymentTestSuiteWithEmbeddedDB {
         Assert.assertTrue(loadedPaymentModelDao.getPaymentNumber() > 0);
         Assert.assertEquals(loadedPaymentModelDao.getPaymentMethodId(), specifiedPaymentModelDao.getPaymentMethodId());
         Assert.assertEquals(loadedPaymentModelDao.getExternalKey(), specifiedPaymentModelDao.getExternalKey());
+
+        // Verify search APIs
+        Assert.assertEquals(ImmutableList.<PaymentModelDao>copyOf(paymentDao.searchPayments(specifiedPaymentModelDao.getPaymentMethodId().toString(), 0L, 100L, internalCallContext).iterator()).size(), 1);
+        Assert.assertEquals(ImmutableList.<PaymentModelDao>copyOf(paymentDao.searchPayments(specifiedPaymentModelDao.getExternalKey(), 0L, 100L, internalCallContext).iterator()).size(), 1);
     }
 
     private void verifyPaymentTransaction(final PaymentTransactionModelDao loadedPaymentTransactionModelDao, final PaymentTransactionModelDao specifiedPaymentTransactionModelDao) {

pom.xml 2(+1 -1)

diff --git a/pom.xml b/pom.xml
index c7699f3..483248d 100644
--- a/pom.xml
+++ b/pom.xml
@@ -20,7 +20,7 @@
     <parent>
         <artifactId>killbill-oss-parent</artifactId>
         <groupId>org.kill-bill.billing</groupId>
-        <version>0.7.21-SNAPSHOT</version>
+        <version>0.7.22</version>
     </parent>
     <artifactId>killbill</artifactId>
     <version>0.11.10-SNAPSHOT</version>
diff --git a/profiles/killbill/pom.xml b/profiles/killbill/pom.xml
index 0277ed7..ee9ae1c 100644
--- a/profiles/killbill/pom.xml
+++ b/profiles/killbill/pom.xml
@@ -32,13 +32,11 @@
             <groupId>ch.qos.logback</groupId>
             <artifactId>logback-classic</artifactId>
             <scope>compile</scope>
-            <optional>true</optional>
         </dependency>
         <dependency>
             <groupId>ch.qos.logback</groupId>
             <artifactId>logback-core</artifactId>
             <scope>compile</scope>
-            <optional>true</optional>
         </dependency>
         <dependency>
             <groupId>com.google.guava</groupId>
@@ -308,20 +306,6 @@
         </resources>
         <plugins>
             <plugin>
-                <groupId>io.tesla.jettyconsole</groupId>
-                <artifactId>jetty-console-maven-plugin</artifactId>
-                <executions>
-                    <execution>
-                        <goals>
-                            <goal>createconsole</goal>
-                        </goals>
-                        <configuration>
-                            <backgroundImage>${basedir}/src/main/jettyconsole/killbill.png</backgroundImage>
-                        </configuration>
-                    </execution>
-                </executions>
-            </plugin>
-            <plugin>
                 <groupId>org.apache.maven.plugins</groupId>
                 <artifactId>maven-jar-plugin</artifactId>
                 <version>2.4</version>
@@ -416,6 +400,20 @@
                     <stopKey>foo</stopKey>
                 </configuration>
             </plugin>
+            <plugin>
+                <groupId>org.simplericity.jettyconsole</groupId>
+                <artifactId>jetty-console-maven-plugin</artifactId>
+                <executions>
+                    <execution>
+                        <goals>
+                            <goal>createconsole</goal>
+                        </goals>
+                        <configuration>
+                            <backgroundImage>${basedir}/src/main/jettyconsole/killbill.png</backgroundImage>
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin>
         </plugins>
     </build>
 </project>
diff --git a/profiles/killbill/src/main/resources/update-checker/killbill-server-update-list.properties b/profiles/killbill/src/main/resources/update-checker/killbill-server-update-list.properties
index f04e249..727ccfe 100644
--- a/profiles/killbill/src/main/resources/update-checker/killbill-server-update-list.properties
+++ b/profiles/killbill/src/main/resources/update-checker/killbill-server-update-list.properties
@@ -3,49 +3,54 @@
 
 ### 0.11.x series ###
 
-## 0.11.9 -- latest unstable release
-0.11.9.updates           =
-0.11.9.notices           = This is the latest dev release.
+## 0.11.10 -- latest unstable release
+0.11.10.updates           =
+0.11.10.notices           = This is the latest dev release.
+0.11.10.release-notes     = http://kill-bill.org
+
+## 0.11.9
+0.11.9.updates           = 0.11.10
+0.11.9.notices           = We recommend upgrading to 0.11.10, our latest dev release.
 0.11.9.release-notes     = http://kill-bill.org
 
 ## 0.11.8
-0.11.8.updates           = 0.11.9
-0.11.8.notices           = We recommend upgrading to 0.11.9, our latest dev release.
+0.11.8.updates           = 0.11.10
+0.11.8.notices           = We recommend upgrading to 0.11.10, our latest dev release.
 0.11.8.release-notes     = http://kill-bill.org
 
 ## 0.11.7
-0.11.7.updates           = 0.11.9
-0.11.7.notices           = We recommend upgrading to 0.11.9, our latest dev release.
+0.11.7.updates           = 0.11.10
+0.11.7.notices           = We recommend upgrading to 0.11.10, our latest dev release.
 0.11.7.release-notes     = http://kill-bill.org
 
 ## 0.11.6
-0.11.6.updates           = 0.11.9
-0.11.6.notices           = We recommend upgrading to 0.11.9, our latest dev release.
+0.11.6.updates           = 0.11.10
+0.11.6.notices           = We recommend upgrading to 0.11.10, our latest dev release.
 0.11.6.release-notes     = http://kill-bill.org
 
 ## 0.11.5
-0.11.5.updates           = 0.11.9
-0.11.5.notices           = We recommend upgrading to 0.11.9, our latest dev release.
+0.11.5.updates           = 0.11.10
+0.11.5.notices           = We recommend upgrading to 0.11.10, our latest dev release.
 0.11.5.release-notes     = http://kill-bill.org
 
 ## 0.11.4
-0.11.4.updates           = 0.11.9
-0.11.4.notices           = We recommend upgrading to 0.11.9, our latest dev release.
+0.11.4.updates           = 0.11.10
+0.11.4.notices           = We recommend upgrading to 0.11.10, our latest dev release.
 0.11.4.release-notes     = http://kill-bill.org
 
 ## 0.11.3
-0.11.3.updates           = 0.11.9
-0.11.3.notices           = We recommend upgrading to 0.11.9, our latest dev release.
+0.11.3.updates           = 0.11.10
+0.11.3.notices           = We recommend upgrading to 0.11.10, our latest dev release.
 0.11.3.release-notes     = http://kill-bill.org
 
 ## 0.11.2
-0.11.2.updates           = 0.11.9
-0.11.2.notices           = We recommend upgrading to 0.11.9, our latest dev release.
+0.11.2.updates           = 0.11.10
+0.11.2.notices           = We recommend upgrading to 0.11.10, our latest dev release.
 0.11.2.release-notes     = http://kill-bill.org
 
 ## 0.11.1
-0.11.1.updates           = 0.11.9
-0.11.1.notices           = We recommend upgrading to 0.11.9, our latest dev release.
+0.11.1.updates           = 0.11.10
+0.11.1.notices           = We recommend upgrading to 0.11.10, our latest dev release.
 0.11.1.release-notes     = http://kill-bill.org
 
 ### 0.10.x series ###
diff --git a/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestPaymentMethod.java b/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestPaymentMethod.java
index 8a21a30..5d3893d 100644
--- a/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestPaymentMethod.java
+++ b/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestPaymentMethod.java
@@ -98,7 +98,7 @@ public class TestPaymentMethod extends TestJaxrsBase {
     }
 
     private void doSearch(final String searchKey, final PaymentMethod paymentMethodJson) throws Exception {
-        final List<PaymentMethod> results1 = killBillClient.searchPaymentMethodsByKey(searchKey);
+        final List<PaymentMethod> results1 = killBillClient.searchPaymentMethodsByKey(searchKey, true);
         Assert.assertEquals(results1.size(), 1);
         Assert.assertEquals(results1.get(0), paymentMethodJson);
 
diff --git a/profiles/killpay/pom.xml b/profiles/killpay/pom.xml
index 4024de5..913b7ab 100644
--- a/profiles/killpay/pom.xml
+++ b/profiles/killpay/pom.xml
@@ -87,6 +87,10 @@
         </dependency>
         <dependency>
             <groupId>org.kill-bill.billing</groupId>
+            <artifactId>killbill-overdue</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.kill-bill.billing</groupId>
             <artifactId>killbill-payment</artifactId>
         </dependency>
         <dependency>
@@ -95,6 +99,10 @@
         </dependency>
         <dependency>
             <groupId>org.kill-bill.billing</groupId>
+            <artifactId>killbill-platform-base</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.kill-bill.billing</groupId>
             <artifactId>killbill-platform-server</artifactId>
             <classifier>classes</classifier>
         </dependency>
@@ -124,6 +132,16 @@
             <artifactId>killbill-embeddeddb-common</artifactId>
         </dependency>
         <dependency>
+            <groupId>org.kill-bill.commons</groupId>
+            <artifactId>killbill-embeddeddb-h2</artifactId>
+            <scope>runtime</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.kill-bill.commons</groupId>
+            <artifactId>killbill-embeddeddb-mysql</artifactId>
+            <scope>runtime</scope>
+        </dependency>
+        <dependency>
             <groupId>org.slf4j</groupId>
             <artifactId>jcl-over-slf4j</artifactId>
         </dependency>
@@ -131,20 +149,6 @@
     <build>
         <plugins>
             <plugin>
-                <groupId>io.tesla.jettyconsole</groupId>
-                <artifactId>jetty-console-maven-plugin</artifactId>
-                <executions>
-                    <execution>
-                        <goals>
-                            <goal>createconsole</goal>
-                        </goals>
-                        <configuration>
-                            <backgroundImage>${basedir}/src/main/jettyconsole/killbill.png</backgroundImage>
-                        </configuration>
-                    </execution>
-                </executions>
-            </plugin>
-            <plugin>
                 <groupId>org.apache.maven.plugins</groupId>
                 <artifactId>maven-jar-plugin</artifactId>
                 <version>2.4</version>
@@ -241,6 +245,20 @@
                     <stopKey>foo</stopKey>
                 </configuration>
             </plugin>
+            <plugin>
+                <groupId>org.simplericity.jettyconsole</groupId>
+                <artifactId>jetty-console-maven-plugin</artifactId>
+                <executions>
+                    <execution>
+                        <goals>
+                            <goal>createconsole</goal>
+                        </goals>
+                        <configuration>
+                            <backgroundImage>${basedir}/src/main/jettyconsole/killbill.png</backgroundImage>
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin>
         </plugins>
     </build>
 </project>
diff --git a/profiles/killpay/src/main/java/org/killbill/billing/server/modules/KillpayServerModule.java b/profiles/killpay/src/main/java/org/killbill/billing/server/modules/KillpayServerModule.java
index fed4c77..2f42574 100644
--- a/profiles/killpay/src/main/java/org/killbill/billing/server/modules/KillpayServerModule.java
+++ b/profiles/killpay/src/main/java/org/killbill/billing/server/modules/KillpayServerModule.java
@@ -38,12 +38,14 @@ import org.killbill.billing.jaxrs.resources.TenantResource;
 import org.killbill.billing.jaxrs.resources.TransactionResource;
 import org.killbill.billing.jaxrs.util.KillbillEventHandler;
 import org.killbill.billing.junction.glue.DefaultJunctionModule;
+import org.killbill.billing.overdue.glue.DefaultOverdueModule;
 import org.killbill.billing.payment.glue.PaymentModule;
 import org.killbill.billing.platform.api.KillbillConfigSource;
 import org.killbill.billing.server.config.KillbillServerConfig;
 import org.killbill.billing.subscription.glue.DefaultSubscriptionModule;
 import org.killbill.billing.tenant.glue.TenantModule;
 import org.killbill.billing.usage.glue.UsageModule;
+import org.killbill.billing.util.email.EmailModule;
 import org.killbill.billing.util.email.templates.TemplateModule;
 import org.killbill.billing.util.glue.AuditModule;
 import org.killbill.billing.util.glue.CacheModule;
@@ -94,6 +96,9 @@ public class KillpayServerModule extends KillbillServerModule {
         install(new DefaultSubscriptionModule(configSource));
         install(new TemplateModule(configSource));
         install(new UsageModule(configSource));
+        // TODO Dependencies for AccountResource
+        install(new DefaultOverdueModule(configSource));
+        install(new EmailModule(configSource));
     }
 
     @Override
diff --git a/profiles/killpay/src/main/resources/update-checker/killbill-server-update-list.properties b/profiles/killpay/src/main/resources/update-checker/killbill-server-update-list.properties
index a56bcc5..e143c38 100644
--- a/profiles/killpay/src/main/resources/update-checker/killbill-server-update-list.properties
+++ b/profiles/killpay/src/main/resources/update-checker/killbill-server-update-list.properties
@@ -3,27 +3,32 @@
 
 ### 0.11.x series ###
 
-## 0.11.9 -- latest unstable release
-0.11.9.updates           =
-0.11.9.notices           = This is the latest dev release.
+## 0.11.10 -- latest unstable release
+0.11.10.updates           =
+0.11.10.notices           = This is the latest dev release.
+0.11.10.release-notes     = http://kill-bill.org
+
+## 0.11.9
+0.11.9.updates           = 0.11.10
+0.11.9.notices           = We recommend upgrading to 0.11.10, our latest dev release.
 0.11.9.release-notes     = http://kill-bill.org
 
 ## 0.11.8
-0.11.8.updates           = 0.11.9
-0.11.8.notices           = We recommend upgrading to 0.11.9, our latest dev release.
+0.11.8.updates           = 0.11.10
+0.11.8.notices           = We recommend upgrading to 0.11.10, our latest dev release.
 0.11.8.release-notes     = http://kill-bill.org
 
 ## 0.11.7
-0.11.7.updates           = 0.11.9
-0.11.7.notices           = We recommend upgrading to 0.11.9, our latest dev release.
+0.11.7.updates           = 0.11.10
+0.11.7.notices           = We recommend upgrading to 0.11.10, our latest dev release.
 0.11.7.release-notes     = http://kill-bill.org
 
 ## 0.11.6
-0.11.6.updates           = 0.11.9
-0.11.6.notices           = We recommend upgrading to 0.11.9, our latest dev release.
+0.11.6.updates           = 0.11.10
+0.11.6.notices           = We recommend upgrading to 0.11.10, our latest dev release.
 0.11.6.release-notes     = http://kill-bill.org
 
 ## 0.11.5
-0.11.5.updates           = 0.11.9
-0.11.5.notices           = We recommend upgrading to 0.11.9, our latest dev release.
+0.11.5.updates           = 0.11.10
+0.11.5.notices           = We recommend upgrading to 0.11.10, our latest dev release.
 0.11.5.release-notes     = http://kill-bill.org
diff --git a/util/src/test/java/org/killbill/billing/util/cache/TestCache.java b/util/src/test/java/org/killbill/billing/util/cache/TestCache.java
index 9b7f436..a65cf7e 100644
--- a/util/src/test/java/org/killbill/billing/util/cache/TestCache.java
+++ b/util/src/test/java/org/killbill/billing/util/cache/TestCache.java
@@ -78,8 +78,11 @@ public class TestCache extends UtilTestSuiteWithEmbeddedDB {
         final Long recordIdFromCache = retrieveRecordIdFromCache(tag.getId());
         Assert.assertNotNull(recordIdFromCache);
 
-        Assert.assertEquals(recordIdFromCache, new Long(1));
-        Assert.assertEquals(tagRecordId, new Long(1));
+        Assert.assertEquals(recordIdFromCache, tagRecordId);
+        // We cannot assume the number to be 1 here as the auto_increment implementation
+        // depends on the database.
+        // See also http://h2database.com/html/grammar.html#create_sequence
+        Assert.assertTrue(recordIdFromCache > 0);
 
         Assert.assertEquals(getCacheSize(CacheType.RECORD_ID), 1);
     }