killbill-aplcache

Merge branch 'control-plugin-api'

6/12/2015 11:01:54 PM

Changes

payment/src/main/java/org/killbill/billing/payment/core/janitor/ErroredPaymentTask.java 160(+0 -160)

payment/src/main/java/org/killbill/billing/payment/core/janitor/PendingTransactionTask.java 65(+0 -65)

pom.xml 2(+1 -1)

Details

diff --git a/account/src/main/java/org/killbill/billing/account/dao/AccountModelDao.java b/account/src/main/java/org/killbill/billing/account/dao/AccountModelDao.java
index e63f7f3..44a9deb 100644
--- a/account/src/main/java/org/killbill/billing/account/dao/AccountModelDao.java
+++ b/account/src/main/java/org/killbill/billing/account/dao/AccountModelDao.java
@@ -25,6 +25,7 @@ import org.joda.time.DateTimeZone;
 import org.killbill.billing.account.api.Account;
 import org.killbill.billing.account.api.AccountData;
 import org.killbill.billing.catalog.api.Currency;
+import org.killbill.billing.util.UUIDs;
 import org.killbill.billing.util.dao.TableName;
 import org.killbill.billing.util.entity.dao.EntityModelDao;
 import org.killbill.billing.util.entity.dao.EntityModelDaoBase;
@@ -96,7 +97,7 @@ public class AccountModelDao extends EntityModelDaoBase implements EntityModelDa
     }
 
     public AccountModelDao(final AccountData account) {
-        this(UUID.randomUUID(), account);
+        this(UUIDs.randomUUID(), account);
     }
 
     public String getExternalKey() {
diff --git a/api/src/main/java/org/killbill/billing/callcontext/InternalCallContext.java b/api/src/main/java/org/killbill/billing/callcontext/InternalCallContext.java
index 5370614..f1429a0 100644
--- a/api/src/main/java/org/killbill/billing/callcontext/InternalCallContext.java
+++ b/api/src/main/java/org/killbill/billing/callcontext/InternalCallContext.java
@@ -69,6 +69,12 @@ public class InternalCallContext extends InternalTenantContext {
              context.getUpdatedDate());
     }
 
+    public InternalCallContext(final InternalCallContext context, final Long accountRecordId, final Long tenantRecordId) {
+        this(tenantRecordId, accountRecordId, context.getUserToken(), context.getCreatedBy(), context.getCallOrigin(),
+             context.getContextUserType(), context.getReasonCode(), context.getComments(), context.getCreatedDate(),
+             context.getUpdatedDate());
+    }
+
     // TODO should not be needed if all services are using internal API
     // Unfortunately not true as some APIs ae hidden in object -- e.g OverdueStateApplicator is doing subscription.cancelEntitlementWithDateOverrideBillingPolicy
     public CallContext toCallContext(final UUID tenantId) {
diff --git a/api/src/main/java/org/killbill/billing/entity/EntityBase.java b/api/src/main/java/org/killbill/billing/entity/EntityBase.java
index 60c8a1b..0c02112 100644
--- a/api/src/main/java/org/killbill/billing/entity/EntityBase.java
+++ b/api/src/main/java/org/killbill/billing/entity/EntityBase.java
@@ -20,6 +20,7 @@ import java.util.UUID;
 
 import org.joda.time.DateTime;
 
+import org.killbill.billing.util.UUIDs;
 import org.killbill.billing.util.entity.Entity;
 
 public abstract class EntityBase implements Entity {
@@ -35,7 +36,7 @@ public abstract class EntityBase implements Entity {
 
     // used to create new objects
     public EntityBase() {
-        this(UUID.randomUUID(), null, null);
+        this(UUIDs.randomUUID(), null, null);
     }
 
     public EntityBase(final UUID id, final DateTime createdDate, final DateTime updatedDate) {
diff --git a/api/src/main/java/org/killbill/billing/junction/DefaultBlockingState.java b/api/src/main/java/org/killbill/billing/junction/DefaultBlockingState.java
index 4ec3117..dedb858 100644
--- a/api/src/main/java/org/killbill/billing/junction/DefaultBlockingState.java
+++ b/api/src/main/java/org/killbill/billing/junction/DefaultBlockingState.java
@@ -24,6 +24,7 @@ import org.joda.time.DateTime;
 import org.killbill.billing.entitlement.api.BlockingState;
 import org.killbill.billing.entitlement.api.BlockingStateType;
 import org.killbill.billing.entity.EntityBase;
+import org.killbill.billing.util.UUIDs;
 import org.killbill.clock.Clock;
 
 public class DefaultBlockingState extends EntityBase implements BlockingState {
@@ -82,7 +83,7 @@ public class DefaultBlockingState extends EntityBase implements BlockingState {
                                 final boolean blockEntitlement,
                                 final boolean blockBilling,
                                 final DateTime effectiveDate) {
-        this(UUID.randomUUID(),
+        this(UUIDs.randomUUID(),
              blockingId,
              type,
              stateName,
diff --git a/api/src/main/java/org/killbill/billing/util/UUIDs.java b/api/src/main/java/org/killbill/billing/util/UUIDs.java
new file mode 100644
index 0000000..d85d9b6
--- /dev/null
+++ b/api/src/main/java/org/killbill/billing/util/UUIDs.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright 2015 The Billing Project, LLC
+ *
+ * The Billing Project licenses this file to you under the Apache License, version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at:
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+package org.killbill.billing.util;
+
+import java.security.SecureRandom;
+import java.util.UUID;
+
+/**
+ * UUIDs helper.
+ *
+ * @author kares
+ */
+public abstract class UUIDs {
+
+    public static UUID randomUUID() { return rndUUIDv4(); }
+
+    private static UUID rndUUIDv4() {
+        // ~ return UUID.randomUUID() :
+        final SecureRandom random = threadRandom.get();
+
+        final byte[] uuid = new byte[16];
+        random.nextBytes(uuid);
+        uuid[6]  &= 0x0f;  /* clear version        */
+        uuid[6]  |= 0x40;  /* set to version 4     */
+        uuid[8]  &= 0x3f;  /* clear variant        */
+        uuid[8]  |= 0x80;  /* set to IETF variant  */
+
+        long msb = 0;
+        msb = (msb << 8) | (uuid[0] & 0xff);
+        msb = (msb << 8) | (uuid[1] & 0xff);
+        msb = (msb << 8) | (uuid[2] & 0xff);
+        msb = (msb << 8) | (uuid[3] & 0xff);
+        msb = (msb << 8) | (uuid[4] & 0xff);
+        msb = (msb << 8) | (uuid[5] & 0xff);
+        msb = (msb << 8) | (uuid[6] & 0xff);
+        msb = (msb << 8) | (uuid[7] & 0xff);
+
+        long lsb = 0;
+        lsb = (lsb << 8) | (uuid[8] & 0xff);
+        lsb = (lsb << 8) | (uuid[9] & 0xff);
+        lsb = (lsb << 8) | (uuid[10] & 0xff);
+        lsb = (lsb << 8) | (uuid[11] & 0xff);
+        lsb = (lsb << 8) | (uuid[12] & 0xff);
+        lsb = (lsb << 8) | (uuid[13] & 0xff);
+        lsb = (lsb << 8) | (uuid[14] & 0xff);
+        lsb = (lsb << 8) | (uuid[15] & 0xff);
+
+        return new UUID(msb, lsb);
+    }
+
+    private static final ThreadLocal<SecureRandom> threadRandom =
+        new ThreadLocal<SecureRandom>() {
+            protected SecureRandom initialValue() {
+                return new SecureRandom();
+            }
+    };
+
+}
diff --git a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestPaymentWithControl.java b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestPaymentWithControl.java
new file mode 100644
index 0000000..9ab369b
--- /dev/null
+++ b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestPaymentWithControl.java
@@ -0,0 +1,205 @@
+/*
+ * Copyright 2014-2015 Groupon, Inc
+ * Copyright 2014-2015 The Billing Project, LLC
+ *
+ * The Billing Project licenses this file to you under the Apache License, version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at:
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.killbill.billing.beatrix.integration;
+
+import java.math.BigDecimal;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.UUID;
+
+import javax.inject.Inject;
+
+import org.killbill.billing.account.api.Account;
+import org.killbill.billing.account.api.AccountData;
+import org.killbill.billing.api.TestApiListener.NextEvent;
+import org.killbill.billing.catalog.api.Currency;
+import org.killbill.billing.osgi.api.OSGIServiceDescriptor;
+import org.killbill.billing.osgi.api.OSGIServiceRegistration;
+import org.killbill.billing.payment.api.Payment;
+import org.killbill.billing.payment.api.PaymentOptions;
+import org.killbill.billing.payment.api.PluginProperty;
+import org.killbill.billing.payment.api.TransactionType;
+import org.killbill.billing.routing.plugin.api.OnFailurePaymentRoutingResult;
+import org.killbill.billing.routing.plugin.api.OnSuccessPaymentRoutingResult;
+import org.killbill.billing.routing.plugin.api.PaymentRoutingApiException;
+import org.killbill.billing.routing.plugin.api.PaymentRoutingContext;
+import org.killbill.billing.routing.plugin.api.PaymentRoutingPluginApi;
+import org.killbill.billing.routing.plugin.api.PriorPaymentRoutingResult;
+import org.testng.Assert;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import com.google.common.base.Predicate;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
+
+public class TestPaymentWithControl extends TestIntegrationBase {
+
+    private final static String TEST_PAYMENT_WITH_CONTROL = "TestPaymentWithControl";
+
+    private TestPaymentRoutingPluginApi testPaymentRoutingWithControl;
+    private List<PluginProperty> properties;
+    private PaymentOptions paymentOptions;
+
+    @Inject
+    private OSGIServiceRegistration<PaymentRoutingPluginApi> pluginRegistry;
+
+    @BeforeClass(groups = "slow")
+    public void beforeClass() throws Exception {
+        super.beforeClass();
+
+        this.testPaymentRoutingWithControl = new TestPaymentRoutingPluginApi();
+        pluginRegistry.registerService(new OSGIServiceDescriptor() {
+            @Override
+            public String getPluginSymbolicName() {
+                return TEST_PAYMENT_WITH_CONTROL;
+            }
+
+            @Override
+            public String getRegistrationName() {
+                return TEST_PAYMENT_WITH_CONTROL;
+            }
+        }, testPaymentRoutingWithControl);
+
+        properties = new ArrayList<PluginProperty>();
+        paymentOptions = new PaymentOptions() {
+            @Override
+            public boolean isExternalPayment() {
+                return false;
+            }
+
+            @Override
+            public List<String> getPaymentControlPluginNames() {
+                return ImmutableList.of(TEST_PAYMENT_WITH_CONTROL);
+            }
+        };
+
+        properties.add(new PluginProperty("name", TEST_PAYMENT_WITH_CONTROL, false));
+
+    }
+
+    @BeforeMethod(groups = "slow")
+    public void beforeMethod() throws Exception {
+        super.beforeMethod();
+        testPaymentRoutingWithControl.reset();
+    }
+
+    @Test(groups = "slow")
+    public void testAuthCaptureWithPaymentControl() throws Exception {
+
+        final AccountData accountData = getAccountData(1);
+        final Account account = createAccountWithNonOsgiPaymentMethod(accountData);
+
+        busHandler.pushExpectedEvents(NextEvent.PAYMENT);
+        final Payment payment = paymentApi.createAuthorizationWithPaymentControl(account, account.getPaymentMethodId(), null, BigDecimal.ONE, account.getCurrency(), null, null,
+                                                                                 properties, paymentOptions, callContext);
+        assertListenerStatus();
+
+        busHandler.pushExpectedEvents(NextEvent.PAYMENT);
+        paymentApi.createCaptureWithPaymentControl(account, payment.getId(), BigDecimal.ONE, account.getCurrency(), null, properties, paymentOptions, callContext);
+        assertListenerStatus();
+
+        Assert.assertEquals(testPaymentRoutingWithControl.getCalls().size(), 2);
+        Assert.assertEquals(testPaymentRoutingWithControl.getCalls().get(TransactionType.AUTHORIZE.toString()), new Integer(1));
+        Assert.assertEquals(testPaymentRoutingWithControl.getCalls().get(TransactionType.CAPTURE.toString()), new Integer(1));
+    }
+
+    @Test(groups = "slow")
+    public void testAuthVoidWithPaymentControl() throws Exception {
+        final AccountData accountData = getAccountData(1);
+        final Account account = createAccountWithNonOsgiPaymentMethod(accountData);
+
+        busHandler.pushExpectedEvents(NextEvent.PAYMENT);
+        final Payment payment = paymentApi.createAuthorizationWithPaymentControl(account, account.getPaymentMethodId(), null, BigDecimal.ONE, account.getCurrency(), null, null,
+                                                                                 properties, paymentOptions, callContext);
+        assertListenerStatus();
+
+        busHandler.pushExpectedEvents(NextEvent.PAYMENT);
+        paymentApi.createVoidWithPaymentControl(account, payment.getId(), null, properties, paymentOptions, callContext);
+        assertListenerStatus();
+        Assert.assertEquals(testPaymentRoutingWithControl.getCalls().size(), 2);
+        Assert.assertEquals(testPaymentRoutingWithControl.getCalls().get(TransactionType.AUTHORIZE.toString()), new Integer(1));
+        Assert.assertEquals(testPaymentRoutingWithControl.getCalls().get(TransactionType.VOID.toString()), new Integer(1));
+    }
+
+    public class TestPaymentRoutingPluginApi implements PaymentRoutingPluginApi {
+
+        private final Map<String, Integer> calls;
+
+        public TestPaymentRoutingPluginApi() {
+            calls = new HashMap<String, Integer>();
+        }
+
+        public Map<String, Integer> getCalls() {
+            return calls;
+        }
+
+        public void reset() {
+            calls.clear();
+        }
+
+        @Override
+        public PriorPaymentRoutingResult priorCall(final PaymentRoutingContext paymentRoutingContext, final Iterable<PluginProperty> properties) throws PaymentRoutingApiException {
+            return new PriorPaymentRoutingResult() {
+                @Override
+                public boolean isAborted() {
+                    return false;
+                }
+                @Override
+                public BigDecimal getAdjustedAmount() {
+                    return null;
+                }
+                @Override
+                public Currency getAdjustedCurrency() {
+                    return null;
+                }
+                @Override
+                public UUID getAdjustedPaymentMethodId() {
+                    return null;
+                }
+                @Override
+                public Iterable<PluginProperty> getAdjustedPluginProperties() {
+                    return null;
+                }
+            };
+        }
+
+        @Override
+        public OnSuccessPaymentRoutingResult onSuccessCall(final PaymentRoutingContext paymentRoutingContext, final Iterable<PluginProperty> properties) throws PaymentRoutingApiException {
+            final PluginProperty nameProperty = Iterables.tryFind(properties, new Predicate<PluginProperty>() {
+                @Override
+                public boolean apply(final PluginProperty input) {
+                    return input.getKey().equals("name");
+                }
+            }).orNull();
+            if (nameProperty != null && nameProperty.getValue().equals(TEST_PAYMENT_WITH_CONTROL)) {
+                final Integer result = calls.get(paymentRoutingContext.getTransactionType());
+                calls.put(paymentRoutingContext.getTransactionType().toString(), result == null ? new Integer(1) : new Integer(result.intValue() + 1));
+            }
+            return new OnSuccessPaymentRoutingResult() {};
+        }
+
+        @Override
+        public OnFailurePaymentRoutingResult onFailureCall(final PaymentRoutingContext paymentRoutingContext, final Iterable<PluginProperty> properties) throws PaymentRoutingApiException {
+            return null;
+        }
+    }
+}
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/api/InvoiceApiHelper.java b/invoice/src/main/java/org/killbill/billing/invoice/api/InvoiceApiHelper.java
index 5cee274..9506d02 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/api/InvoiceApiHelper.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/api/InvoiceApiHelper.java
@@ -36,6 +36,7 @@ import org.killbill.billing.invoice.dao.InvoiceItemModelDao;
 import org.killbill.billing.invoice.dao.InvoiceModelDao;
 import org.killbill.billing.invoice.model.InvoiceItemFactory;
 import org.killbill.billing.invoice.model.ItemAdjInvoiceItem;
+import org.killbill.billing.util.UUIDs;
 import org.killbill.billing.util.callcontext.CallContext;
 import org.killbill.billing.util.callcontext.InternalCallContextFactory;
 import org.killbill.billing.util.globallocker.LockerType;
@@ -140,7 +141,7 @@ public class InvoiceApiHelper {
         }
 
         // Finally, create the adjustment
-        return new ItemAdjInvoiceItem(UUID.randomUUID(),
+        return new ItemAdjInvoiceItem(UUIDs.randomUUID(),
                                       context.getCreatedDate(),
                                       invoiceItemToBeAdjusted.getInvoiceId(),
                                       invoiceItemToBeAdjusted.getAccountId(),
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/api/migration/MigrationInvoice.java b/invoice/src/main/java/org/killbill/billing/invoice/api/migration/MigrationInvoice.java
index 16832f6..fbda4e7 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/api/migration/MigrationInvoice.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/api/migration/MigrationInvoice.java
@@ -22,10 +22,11 @@ import org.joda.time.LocalDate;
 
 import org.killbill.billing.catalog.api.Currency;
 import org.killbill.billing.invoice.model.DefaultInvoice;
+import org.killbill.billing.util.UUIDs;
 
 public class MigrationInvoice extends DefaultInvoice {
 
     public MigrationInvoice(final UUID accountId, final LocalDate invoiceDate, final LocalDate targetDate, final Currency currency) {
-        super(UUID.randomUUID(), accountId, null, invoiceDate, targetDate, currency, true);
+        super(UUIDs.randomUUID(), accountId, null, invoiceDate, targetDate, currency, true);
     }
 }
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/api/user/DefaultInvoiceUserApi.java b/invoice/src/main/java/org/killbill/billing/invoice/api/user/DefaultInvoiceUserApi.java
index fc0b942..42d8f7c 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/api/user/DefaultInvoiceUserApi.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/api/user/DefaultInvoiceUserApi.java
@@ -55,6 +55,7 @@ import org.killbill.billing.invoice.model.InvoiceItemFactory;
 import org.killbill.billing.invoice.template.HtmlInvoice;
 import org.killbill.billing.invoice.template.HtmlInvoiceGenerator;
 import org.killbill.billing.tag.TagInternalApi;
+import org.killbill.billing.util.UUIDs;
 import org.killbill.billing.util.api.TagApiException;
 import org.killbill.billing.util.callcontext.CallContext;
 import org.killbill.billing.util.callcontext.InternalCallContextFactory;
@@ -291,7 +292,7 @@ public class DefaultInvoiceUserApi implements InvoiceUserApi {
                         invoiceForExternalCharge = existingInvoicesForExternalCharges.get(invoiceIdForExternalCharge);
                     }
 
-                    final InvoiceItem externalCharge = new ExternalChargeInvoiceItem(UUID.randomUUID(),
+                    final InvoiceItem externalCharge = new ExternalChargeInvoiceItem(UUIDs.randomUUID(),
                                                                                      context.getCreatedDate(),
                                                                                      invoiceForExternalCharge.getId(),
                                                                                      accountId,
@@ -349,7 +350,7 @@ public class DefaultInvoiceUserApi implements InvoiceUserApi {
                 }
 
                 // Create the new credit
-                creditItem = new CreditAdjInvoiceItem(UUID.randomUUID(),
+                creditItem = new CreditAdjInvoiceItem(UUIDs.randomUUID(),
                                                       context.getCreatedDate(),
                                                       invoiceForCredit.getId(),
                                                       accountId,
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/dao/DefaultInvoiceDao.java b/invoice/src/main/java/org/killbill/billing/invoice/dao/DefaultInvoiceDao.java
index 742f3f3..655c5da 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/dao/DefaultInvoiceDao.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/dao/DefaultInvoiceDao.java
@@ -43,6 +43,7 @@ import org.killbill.billing.invoice.api.InvoiceItemType;
 import org.killbill.billing.invoice.api.InvoicePaymentType;
 import org.killbill.billing.invoice.api.user.DefaultInvoiceAdjustmentEvent;
 import org.killbill.billing.invoice.notification.NextBillingDatePoster;
+import org.killbill.billing.util.UUIDs;
 import org.killbill.billing.util.cache.CacheControllerDispatcher;
 import org.killbill.billing.util.callcontext.InternalCallContextFactory;
 import org.killbill.billing.util.config.InvoiceConfig;
@@ -458,7 +459,7 @@ public class DefaultInvoiceDao extends EntityDaoBase<InvoiceModelDao, Invoice, I
                     return existingRefund;
                 }
 
-                final InvoicePaymentModelDao refund = new InvoicePaymentModelDao(UUID.randomUUID(), context.getCreatedDate(), InvoicePaymentType.REFUND,
+                final InvoicePaymentModelDao refund = new InvoicePaymentModelDao(UUIDs.randomUUID(), context.getCreatedDate(), InvoicePaymentType.REFUND,
                                                                                  payment.getInvoiceId(), paymentId,
                                                                                  context.getCreatedDate(), requestedPositiveAmount.negate(),
                                                                                  payment.getCurrency(), payment.getProcessedCurrency(), transactionExternalKey, payment.getId());
@@ -544,7 +545,7 @@ public class DefaultInvoiceDao extends EntityDaoBase<InvoiceModelDao, Invoice, I
                 if (payment == null) {
                     throw new InvoiceApiException(ErrorCode.INVOICE_PAYMENT_NOT_FOUND, invoicePaymentId.toString());
                 }
-                final InvoicePaymentModelDao chargeBack = new InvoicePaymentModelDao(UUID.randomUUID(), context.getCreatedDate(), InvoicePaymentType.CHARGED_BACK,
+                final InvoicePaymentModelDao chargeBack = new InvoicePaymentModelDao(UUIDs.randomUUID(), context.getCreatedDate(), InvoicePaymentType.CHARGED_BACK,
                                                                                      payment.getInvoiceId(), payment.getPaymentId(), context.getCreatedDate(),
                                                                                      requestedChargedBackAmount.negate(), payment.getCurrency(), payment.getProcessedCurrency(),
                                                                                      null, payment.getId());
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/dao/InvoiceItemModelDao.java b/invoice/src/main/java/org/killbill/billing/invoice/dao/InvoiceItemModelDao.java
index e7a3a2a..9254e11 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/dao/InvoiceItemModelDao.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/dao/InvoiceItemModelDao.java
@@ -22,9 +22,9 @@ import java.util.UUID;
 import org.joda.time.DateTime;
 import org.joda.time.LocalDate;
 import org.killbill.billing.catalog.api.Currency;
-import org.killbill.billing.entity.EntityBase;
 import org.killbill.billing.invoice.api.InvoiceItem;
 import org.killbill.billing.invoice.api.InvoiceItemType;
+import org.killbill.billing.util.UUIDs;
 import org.killbill.billing.util.dao.TableName;
 import org.killbill.billing.util.entity.dao.EntityModelDao;
 import org.killbill.billing.util.entity.dao.EntityModelDaoBase;
@@ -75,7 +75,7 @@ public class InvoiceItemModelDao extends EntityModelDaoBase implements EntityMod
                                final UUID bundleId, final UUID subscriptionId, final String description, final String planName,
                                final String phaseName, final String usageName, final LocalDate startDate, final LocalDate endDate, final BigDecimal amount,
                                final BigDecimal rate, final Currency currency, final UUID linkedItemId) {
-        this(UUID.randomUUID(), createdDate, type, invoiceId, accountId, bundleId, subscriptionId, description, planName, phaseName, usageName,
+        this(UUIDs.randomUUID(), createdDate, type, invoiceId, accountId, bundleId, subscriptionId, description, planName, phaseName, usageName,
              startDate, endDate, amount, rate, currency, linkedItemId);
     }
 
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/dao/InvoiceModelDao.java b/invoice/src/main/java/org/killbill/billing/invoice/dao/InvoiceModelDao.java
index 81f5311..5d62bf0 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/dao/InvoiceModelDao.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/dao/InvoiceModelDao.java
@@ -27,8 +27,8 @@ import org.joda.time.LocalDate;
 
 import org.killbill.billing.catalog.api.Currency;
 import org.killbill.billing.invoice.api.Invoice;
+import org.killbill.billing.util.UUIDs;
 import org.killbill.billing.util.dao.TableName;
-import org.killbill.billing.entity.EntityBase;
 import org.killbill.billing.util.entity.dao.EntityModelDao;
 import org.killbill.billing.util.entity.dao.EntityModelDaoBase;
 
@@ -61,11 +61,11 @@ public class InvoiceModelDao extends EntityModelDaoBase implements EntityModelDa
     }
 
     public InvoiceModelDao(final UUID accountId, final LocalDate invoiceDate, final LocalDate targetDate, final Currency currency, final boolean migrated) {
-        this(UUID.randomUUID(), null, accountId, null, invoiceDate, targetDate, currency, migrated);
+        this(UUIDs.randomUUID(), null, accountId, null, invoiceDate, targetDate, currency, migrated);
     }
 
     public InvoiceModelDao(final UUID accountId, final LocalDate invoiceDate, final LocalDate targetDate, final Currency currency) {
-        this(UUID.randomUUID(), null, accountId, null, invoiceDate, targetDate, currency, false);
+        this(UUIDs.randomUUID(), null, accountId, null, invoiceDate, targetDate, currency, false);
     }
 
     public InvoiceModelDao(final Invoice invoice) {
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/model/CreditAdjInvoiceItem.java b/invoice/src/main/java/org/killbill/billing/invoice/model/CreditAdjInvoiceItem.java
index 5f84d83..aa9938a 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/model/CreditAdjInvoiceItem.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/model/CreditAdjInvoiceItem.java
@@ -27,6 +27,7 @@ import org.joda.time.DateTime;
 import org.joda.time.LocalDate;
 import org.killbill.billing.catalog.api.Currency;
 import org.killbill.billing.invoice.api.InvoiceItemType;
+import org.killbill.billing.util.UUIDs;
 
 import com.google.common.base.Objects;
 
@@ -34,7 +35,7 @@ public class CreditAdjInvoiceItem extends AdjInvoiceItem {
 
     public CreditAdjInvoiceItem(final UUID invoiceId, final UUID accountId, final LocalDate date,
                                 final BigDecimal amount, final Currency currency) {
-        this(UUID.randomUUID(), null, invoiceId, accountId, date, amount, currency);
+        this(UUIDs.randomUUID(), null, invoiceId, accountId, date, amount, currency);
     }
 
     public CreditAdjInvoiceItem(final UUID id, @Nullable final DateTime createdDate, final UUID invoiceId, final UUID accountId, final LocalDate date,
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/model/CreditBalanceAdjInvoiceItem.java b/invoice/src/main/java/org/killbill/billing/invoice/model/CreditBalanceAdjInvoiceItem.java
index 598249d..5dfe7cc 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/model/CreditBalanceAdjInvoiceItem.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/model/CreditBalanceAdjInvoiceItem.java
@@ -27,12 +27,13 @@ import org.joda.time.DateTime;
 import org.joda.time.LocalDate;
 import org.killbill.billing.catalog.api.Currency;
 import org.killbill.billing.invoice.api.InvoiceItemType;
+import org.killbill.billing.util.UUIDs;
 
 public class CreditBalanceAdjInvoiceItem extends AdjInvoiceItem {
 
     public CreditBalanceAdjInvoiceItem(final UUID invoiceId, final UUID accountId,
                                        final LocalDate date, final BigDecimal amount, final Currency currency) {
-        this(UUID.randomUUID(), null, invoiceId, accountId, date, null, amount, currency);
+        this(UUIDs.randomUUID(), null, invoiceId, accountId, date, null, amount, currency);
     }
 
     public CreditBalanceAdjInvoiceItem(final UUID id, @Nullable final DateTime createdDate, final UUID invoiceId, final UUID accountId,
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/model/DefaultInvoice.java b/invoice/src/main/java/org/killbill/billing/invoice/model/DefaultInvoice.java
index 15880ec..8baaefa 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/model/DefaultInvoice.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/model/DefaultInvoice.java
@@ -36,6 +36,7 @@ import org.killbill.billing.invoice.calculator.InvoiceCalculatorUtils;
 import org.killbill.billing.invoice.dao.InvoiceItemModelDao;
 import org.killbill.billing.invoice.dao.InvoiceModelDao;
 import org.killbill.billing.invoice.dao.InvoicePaymentModelDao;
+import org.killbill.billing.util.UUIDs;
 
 import com.google.common.base.Function;
 import com.google.common.collect.Collections2;
@@ -55,7 +56,7 @@ public class DefaultInvoice extends EntityBase implements Invoice, Cloneable {
 
     // Used to create a new invoice
     public DefaultInvoice(final UUID accountId, final LocalDate invoiceDate, final LocalDate targetDate, final Currency currency) {
-        this(UUID.randomUUID(), accountId, null, invoiceDate, targetDate, currency, false);
+        this(UUIDs.randomUUID(), accountId, null, invoiceDate, targetDate, currency, false);
     }
 
     public DefaultInvoice(final UUID invoiceId, final UUID accountId, @Nullable final Integer invoiceNumber, final LocalDate invoiceDate,
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/model/DefaultInvoicePayment.java b/invoice/src/main/java/org/killbill/billing/invoice/model/DefaultInvoicePayment.java
index edca219..abb8673 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/model/DefaultInvoicePayment.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/model/DefaultInvoicePayment.java
@@ -28,6 +28,7 @@ import org.killbill.billing.invoice.api.InvoicePayment;
 import org.killbill.billing.invoice.api.InvoicePaymentType;
 import org.killbill.billing.invoice.dao.InvoicePaymentModelDao;
 import org.killbill.billing.entity.EntityBase;
+import org.killbill.billing.util.UUIDs;
 
 public class DefaultInvoicePayment extends EntityBase implements InvoicePayment {
 
@@ -43,7 +44,7 @@ public class DefaultInvoicePayment extends EntityBase implements InvoicePayment 
 
     public DefaultInvoicePayment(final InvoicePaymentType type, final UUID paymentId, final UUID invoiceId, final DateTime paymentDate,
                                  final BigDecimal amount, final Currency currency, final Currency processedCurrency) {
-        this(UUID.randomUUID(), null, type, paymentId, invoiceId, paymentDate, amount, currency, processedCurrency, null, null);
+        this(UUIDs.randomUUID(), null, type, paymentId, invoiceId, paymentDate, amount, currency, processedCurrency, null, null);
     }
 
     public DefaultInvoicePayment(final UUID id, final InvoicePaymentType type, final UUID paymentId, final UUID invoiceId, final DateTime paymentDate,
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/model/ExternalChargeInvoiceItem.java b/invoice/src/main/java/org/killbill/billing/invoice/model/ExternalChargeInvoiceItem.java
index 67821b6..f97eeff 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/model/ExternalChargeInvoiceItem.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/model/ExternalChargeInvoiceItem.java
@@ -25,12 +25,13 @@ import org.joda.time.DateTime;
 import org.joda.time.LocalDate;
 import org.killbill.billing.catalog.api.Currency;
 import org.killbill.billing.invoice.api.InvoiceItemType;
+import org.killbill.billing.util.UUIDs;
 
 public class ExternalChargeInvoiceItem extends InvoiceItemBase {
 
     public ExternalChargeInvoiceItem(final UUID invoiceId, final UUID accountId, @Nullable final UUID bundleId, @Nullable final String description,
                                      final LocalDate date, final BigDecimal amount, final Currency currency) {
-        this(UUID.randomUUID(), invoiceId, accountId, bundleId, description, date, amount, currency);
+        this(UUIDs.randomUUID(), invoiceId, accountId, bundleId, description, date, amount, currency);
     }
 
     public ExternalChargeInvoiceItem(final UUID id, final UUID invoiceId, final UUID accountId, @Nullable final UUID bundleId,
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/model/FixedPriceInvoiceItem.java b/invoice/src/main/java/org/killbill/billing/invoice/model/FixedPriceInvoiceItem.java
index 590ba09..9a29b6f 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/model/FixedPriceInvoiceItem.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/model/FixedPriceInvoiceItem.java
@@ -27,13 +27,14 @@ import org.joda.time.DateTime;
 import org.joda.time.LocalDate;
 import org.killbill.billing.catalog.api.Currency;
 import org.killbill.billing.invoice.api.InvoiceItemType;
+import org.killbill.billing.util.UUIDs;
 
 public class FixedPriceInvoiceItem extends InvoiceItemBase {
 
     public FixedPriceInvoiceItem(final UUID invoiceId, final UUID accountId, @Nullable final UUID bundleId, @Nullable final UUID subscriptionId,
                                  final String planName, final String phaseName,
                                  final LocalDate date, final BigDecimal amount, final Currency currency) {
-        this(UUID.randomUUID(), null, invoiceId, accountId, bundleId, subscriptionId, planName, phaseName, null, date, amount, currency);
+        this(UUIDs.randomUUID(), null, invoiceId, accountId, bundleId, subscriptionId, planName, phaseName, null, date, amount, currency);
     }
 
     public FixedPriceInvoiceItem(final UUID id, @Nullable final DateTime createdDate, final UUID invoiceId, final UUID accountId, final UUID bundleId,
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/model/ItemAdjInvoiceItem.java b/invoice/src/main/java/org/killbill/billing/invoice/model/ItemAdjInvoiceItem.java
index ac3575f..ff2387a 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/model/ItemAdjInvoiceItem.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/model/ItemAdjInvoiceItem.java
@@ -28,6 +28,7 @@ import org.joda.time.LocalDate;
 import org.killbill.billing.catalog.api.Currency;
 import org.killbill.billing.invoice.api.InvoiceItem;
 import org.killbill.billing.invoice.api.InvoiceItemType;
+import org.killbill.billing.util.UUIDs;
 
 import com.google.common.base.Objects;
 
@@ -35,7 +36,7 @@ public class ItemAdjInvoiceItem extends AdjInvoiceItem {
 
     public ItemAdjInvoiceItem(final InvoiceItem invoiceItem, final LocalDate effectiveDate,
                               final BigDecimal amount, final Currency currency) {
-        this(UUID.randomUUID(), invoiceItem.getInvoiceId(), invoiceItem.getAccountId(), effectiveDate,
+        this(UUIDs.randomUUID(), invoiceItem.getInvoiceId(), invoiceItem.getAccountId(), effectiveDate,
              amount, currency, invoiceItem.getId());
     }
 
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/model/RecurringInvoiceItem.java b/invoice/src/main/java/org/killbill/billing/invoice/model/RecurringInvoiceItem.java
index d82da43..b63271b 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/model/RecurringInvoiceItem.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/model/RecurringInvoiceItem.java
@@ -27,6 +27,7 @@ import org.joda.time.DateTime;
 import org.joda.time.LocalDate;
 import org.killbill.billing.catalog.api.Currency;
 import org.killbill.billing.invoice.api.InvoiceItemType;
+import org.killbill.billing.util.UUIDs;
 
 import com.google.common.base.Objects;
 
@@ -35,7 +36,7 @@ public class RecurringInvoiceItem extends InvoiceItemBase {
     public RecurringInvoiceItem(final UUID invoiceId, final UUID accountId, final UUID bundleId, final UUID subscriptionId,
                                 final String planName, final String phaseName, final LocalDate startDate, final LocalDate endDate,
                                 final BigDecimal amount, final BigDecimal rate, final Currency currency) {
-        this(UUID.randomUUID(), null, invoiceId, accountId, bundleId, subscriptionId, planName, phaseName, startDate, endDate, amount, rate, currency);
+        this(UUIDs.randomUUID(), null, invoiceId, accountId, bundleId, subscriptionId, planName, phaseName, startDate, endDate, amount, rate, currency);
     }
 
     public RecurringInvoiceItem(final UUID id, @Nullable final DateTime createdDate, final UUID invoiceId, final UUID accountId, final UUID bundleId, final UUID subscriptionId,
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/model/RefundAdjInvoiceItem.java b/invoice/src/main/java/org/killbill/billing/invoice/model/RefundAdjInvoiceItem.java
index 0f9e3c0..a0e4dd4 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/model/RefundAdjInvoiceItem.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/model/RefundAdjInvoiceItem.java
@@ -27,6 +27,7 @@ import org.joda.time.DateTime;
 import org.joda.time.LocalDate;
 import org.killbill.billing.catalog.api.Currency;
 import org.killbill.billing.invoice.api.InvoiceItemType;
+import org.killbill.billing.util.UUIDs;
 
 import com.google.common.base.Objects;
 
@@ -34,7 +35,7 @@ public class RefundAdjInvoiceItem extends AdjInvoiceItem {
 
     public RefundAdjInvoiceItem(final UUID invoiceId, final UUID accountId, final LocalDate date,
                                 final BigDecimal amount, final Currency currency) {
-        this(UUID.randomUUID(), null, invoiceId, accountId, date, amount, currency);
+        this(UUIDs.randomUUID(), null, invoiceId, accountId, date, amount, currency);
     }
 
     public RefundAdjInvoiceItem(final UUID id, @Nullable final DateTime createdDate, final UUID invoiceId, final UUID accountId, final LocalDate date,
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/model/RepairAdjInvoiceItem.java b/invoice/src/main/java/org/killbill/billing/invoice/model/RepairAdjInvoiceItem.java
index cd7fcab..348fdc0 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/model/RepairAdjInvoiceItem.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/model/RepairAdjInvoiceItem.java
@@ -27,6 +27,7 @@ import org.joda.time.DateTime;
 import org.joda.time.LocalDate;
 import org.killbill.billing.catalog.api.Currency;
 import org.killbill.billing.invoice.api.InvoiceItemType;
+import org.killbill.billing.util.UUIDs;
 
 import com.google.common.base.Objects;
 
@@ -34,7 +35,7 @@ public class RepairAdjInvoiceItem extends AdjInvoiceItem {
 
     public RepairAdjInvoiceItem(final UUID invoiceId, final UUID accountId, final LocalDate startDate, final LocalDate endDate,
                                 final BigDecimal amount, final Currency currency, final UUID reversingId) {
-        this(UUID.randomUUID(), null, invoiceId, accountId, startDate, endDate, amount, currency, reversingId);
+        this(UUIDs.randomUUID(), null, invoiceId, accountId, startDate, endDate, amount, currency, reversingId);
     }
 
     public RepairAdjInvoiceItem(final UUID id, @Nullable final DateTime createdDate, final UUID invoiceId, final UUID accountId, final LocalDate startDate, final LocalDate endDate,
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/model/TaxInvoiceItem.java b/invoice/src/main/java/org/killbill/billing/invoice/model/TaxInvoiceItem.java
index 16d0970..0b1396f 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/model/TaxInvoiceItem.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/model/TaxInvoiceItem.java
@@ -25,12 +25,13 @@ import org.joda.time.DateTime;
 import org.joda.time.LocalDate;
 import org.killbill.billing.catalog.api.Currency;
 import org.killbill.billing.invoice.api.InvoiceItemType;
+import org.killbill.billing.util.UUIDs;
 
 public class TaxInvoiceItem extends InvoiceItemBase {
 
     public TaxInvoiceItem(final UUID invoiceId, final UUID accountId, @Nullable final UUID bundleId, @Nullable final String description,
                           final LocalDate date, final BigDecimal amount, final Currency currency) {
-        this(UUID.randomUUID(), invoiceId, accountId, bundleId, description, date, amount, currency);
+        this(UUIDs.randomUUID(), invoiceId, accountId, bundleId, description, date, amount, currency);
     }
 
     public TaxInvoiceItem(final UUID id, final UUID invoiceId, final UUID accountId, @Nullable final UUID bundleId,
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/model/UsageInvoiceItem.java b/invoice/src/main/java/org/killbill/billing/invoice/model/UsageInvoiceItem.java
index 5233503..0763e30 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/model/UsageInvoiceItem.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/model/UsageInvoiceItem.java
@@ -26,6 +26,7 @@ import org.joda.time.DateTime;
 import org.joda.time.LocalDate;
 import org.killbill.billing.catalog.api.Currency;
 import org.killbill.billing.invoice.api.InvoiceItemType;
+import org.killbill.billing.util.UUIDs;
 
 import com.google.common.base.Objects;
 
@@ -34,7 +35,7 @@ public class UsageInvoiceItem extends InvoiceItemBase {
     public UsageInvoiceItem(final UUID invoiceId, final UUID accountId, @Nullable final UUID bundleId, @Nullable final UUID subscriptionId,
                             final String planName, final String phaseName, final String usageName,
                             final LocalDate startDate, final LocalDate endDate, final BigDecimal amount, final Currency currency) {
-        this(UUID.randomUUID(), null, invoiceId, accountId, bundleId, subscriptionId, planName, phaseName, usageName, startDate, endDate, null, amount, currency);
+        this(UUIDs.randomUUID(), null, invoiceId, accountId, bundleId, subscriptionId, planName, phaseName, usageName, startDate, endDate, null, amount, currency);
     }
 
     public UsageInvoiceItem(final UUID id, @Nullable final DateTime createdDate, final UUID invoiceId, final UUID accountId, final UUID bundleId,
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/AdminPaymentJson.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/AdminPaymentJson.java
new file mode 100644
index 0000000..3ed676e
--- /dev/null
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/AdminPaymentJson.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2014-2015 Groupon, Inc
+ * Copyright 2014-2015 The Billing Project, LLC
+ *
+ * The Billing Project licenses this file to you under the Apache License, version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at:
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.killbill.billing.jaxrs.json;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+public class AdminPaymentJson {
+
+    private final String lastSuccessPaymentState;
+    private final String currentPaymentStateName;
+    private final String transactionStatus;
+
+    @JsonCreator
+    public AdminPaymentJson(@JsonProperty("lastSuccessPaymentState") final String lastSuccessPaymentState,
+                            @JsonProperty("currentPaymentStateName") final String currentPaymentStateName,
+                            @JsonProperty("transactionStatus") final String transactionStatus) {
+        this.lastSuccessPaymentState = lastSuccessPaymentState;
+        this.currentPaymentStateName = currentPaymentStateName;
+        this.transactionStatus = transactionStatus;
+    }
+
+    public String getLastSuccessPaymentState() {
+        return lastSuccessPaymentState;
+    }
+
+    public String getCurrentPaymentStateName() {
+        return currentPaymentStateName;
+    }
+
+    public String getTransactionStatus() {
+        return transactionStatus;
+    }
+
+    @Override
+    public String toString() {
+        return "AdminPaymentJson{" +
+               "lastSuccessPaymentState='" + lastSuccessPaymentState + '\'' +
+               ", currentPaymentStateName='" + currentPaymentStateName + '\'' +
+               ", transactionStatus='" + transactionStatus + '\'' +
+               '}';
+    }
+}
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/AccountResource.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/AccountResource.java
index 3f0c878..707deff 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/AccountResource.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/AccountResource.java
@@ -86,6 +86,7 @@ import org.killbill.billing.payment.api.PaymentMethod;
 import org.killbill.billing.payment.api.PaymentOptions;
 import org.killbill.billing.payment.api.PluginProperty;
 import org.killbill.billing.payment.api.TransactionType;
+import org.killbill.billing.util.UUIDs;
 import org.killbill.billing.util.api.AuditLevel;
 import org.killbill.billing.util.api.AuditUserApi;
 import org.killbill.billing.util.api.CustomFieldApiException;
@@ -969,7 +970,7 @@ public class AccountResource extends JaxRsResourceBase {
                                                                           )
                                                     .orNull();
         if (existingEmail == null) {
-            accountUserApi.addEmail(accountId, json.toAccountEmail(UUID.randomUUID()), callContext);
+            accountUserApi.addEmail(accountId, json.toAccountEmail(UUIDs.randomUUID()), callContext);
         }
 
         return uriBuilder.buildResponse(uriInfo, AccountResource.class, "getEmails", json.getAccountId());
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/AdminResource.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/AdminResource.java
new file mode 100644
index 0000000..ed98edb
--- /dev/null
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/AdminResource.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright 2014-2015 Groupon, Inc
+ * Copyright 2014-2015 The Billing Project, LLC
+ *
+ * The Billing Project licenses this file to you under the Apache License, version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at:
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.killbill.billing.jaxrs.resources;
+
+import java.util.UUID;
+
+import javax.inject.Inject;
+import javax.servlet.http.HttpServletRequest;
+import javax.ws.rs.Consumes;
+import javax.ws.rs.HeaderParam;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.Response.Status;
+
+import org.killbill.billing.account.api.AccountUserApi;
+import org.killbill.billing.jaxrs.json.AdminPaymentJson;
+import org.killbill.billing.jaxrs.util.Context;
+import org.killbill.billing.jaxrs.util.JaxrsUriBuilder;
+import org.killbill.billing.payment.api.AdminPaymentApi;
+import org.killbill.billing.payment.api.Payment;
+import org.killbill.billing.payment.api.PaymentApi;
+import org.killbill.billing.payment.api.PaymentApiException;
+import org.killbill.billing.payment.api.PaymentTransaction;
+import org.killbill.billing.payment.api.PluginProperty;
+import org.killbill.billing.payment.api.TransactionStatus;
+import org.killbill.billing.util.api.AuditUserApi;
+import org.killbill.billing.util.api.CustomFieldUserApi;
+import org.killbill.billing.util.api.TagUserApi;
+import org.killbill.billing.util.callcontext.CallContext;
+import org.killbill.clock.Clock;
+
+import com.google.common.base.Predicate;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
+import com.google.inject.Singleton;
+import com.wordnik.swagger.annotations.Api;
+import com.wordnik.swagger.annotations.ApiOperation;
+import com.wordnik.swagger.annotations.ApiResponse;
+import com.wordnik.swagger.annotations.ApiResponses;
+
+import static javax.ws.rs.core.MediaType.APPLICATION_JSON;
+
+@Singleton
+@Path(JaxrsResource.ADMIN_PATH)
+@Api(value = JaxrsResource.ADMIN_PATH, description = "Admin operations (will require special privileges)")
+public class AdminResource extends JaxRsResourceBase {
+
+    private final AdminPaymentApi adminPaymentApi;
+
+    @Inject
+    public AdminResource(final JaxrsUriBuilder uriBuilder, final TagUserApi tagUserApi, final CustomFieldUserApi customFieldUserApi, final AuditUserApi auditUserApi, final AccountUserApi accountUserApi, final PaymentApi paymentApi, final AdminPaymentApi adminPaymentApi, final Clock clock, final Context context) {
+        super(uriBuilder, tagUserApi, customFieldUserApi, auditUserApi, accountUserApi, paymentApi, clock, context);
+        this.adminPaymentApi = adminPaymentApi;
+    }
+
+
+    @PUT
+    @Consumes(APPLICATION_JSON)
+    @Produces(APPLICATION_JSON)
+    @Path("/payments/{paymentId:" + UUID_PATTERN + "}/transactions/{paymentTransactionId:" + UUID_PATTERN + "}")
+    @ApiOperation(value = "Update existing paymentTransaction and associated payment state")
+    @ApiResponses(value = {@ApiResponse(code = 400, message = "Invalid account data supplied")})
+    public Response updatePaymentTransactionState(final AdminPaymentJson json,
+                                                  @PathParam("paymentId") final String paymentIdStr,
+                                                  @PathParam("paymentTransactionId") final String paymentTransactionIdStr,
+                                                  @HeaderParam(HDR_CREATED_BY) final String createdBy,
+                                                  @HeaderParam(HDR_REASON) final String reason,
+                                                  @HeaderParam(HDR_COMMENT) final String comment,
+                                                  @javax.ws.rs.core.Context final HttpServletRequest request) throws PaymentApiException {
+
+        final CallContext callContext = context.createContext(createdBy, reason, comment, request);
+
+        final Payment payment = paymentApi.getPayment(UUID.fromString(paymentIdStr), false, ImmutableList.<PluginProperty>of(), callContext);
+
+        final UUID paymentTransactionId = UUID.fromString(paymentTransactionIdStr);
+
+        final PaymentTransaction paymentTransaction = Iterables.tryFind(payment.getTransactions(), new Predicate<PaymentTransaction>() {
+            @Override
+            public boolean apply(final PaymentTransaction input) {
+                return input.getId().equals(paymentTransactionId);
+            }
+        }).orNull();
+
+        adminPaymentApi.fixPaymentTransactionState(payment, paymentTransaction, TransactionStatus.valueOf(json.getTransactionStatus()),
+                                                   json.getLastSuccessPaymentState(), json.getCurrentPaymentStateName(), ImmutableList.<PluginProperty>of(), callContext);
+        return Response.status(Status.OK).build();
+    }
+
+}
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/InvoicePaymentResource.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/InvoicePaymentResource.java
index 3a68362..adff94f 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/InvoicePaymentResource.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/InvoicePaymentResource.java
@@ -56,6 +56,7 @@ import org.killbill.billing.payment.api.Payment;
 import org.killbill.billing.payment.api.PaymentApi;
 import org.killbill.billing.payment.api.PaymentApiException;
 import org.killbill.billing.payment.api.PluginProperty;
+import org.killbill.billing.util.UUIDs;
 import org.killbill.billing.util.api.AuditUserApi;
 import org.killbill.billing.util.api.CustomFieldApiException;
 import org.killbill.billing.util.api.CustomFieldUserApi;
@@ -157,7 +158,7 @@ public class InvoicePaymentResource extends JaxRsResourceBase {
         final Account account = accountUserApi.getAccountById(payment.getAccountId(), callContext);
 
         final Iterable<PluginProperty> pluginProperties;
-        final String transactionExternalKey = json.getTransactionExternalKey() != null ? json.getTransactionExternalKey() : UUID.randomUUID().toString();
+        final String transactionExternalKey = json.getTransactionExternalKey() != null ? json.getTransactionExternalKey() : UUIDs.randomUUID().toString();
         if (json.isAdjusted() != null && json.isAdjusted()) {
             if (json.getAdjustments() != null && json.getAdjustments().size() > 0) {
                 final Map<UUID, BigDecimal> adjustments = new HashMap<UUID, BigDecimal>();
@@ -202,7 +203,7 @@ public class InvoicePaymentResource extends JaxRsResourceBase {
         final UUID paymentUuid = UUID.fromString(paymentId);
         final Payment payment = paymentApi.getPayment(paymentUuid, false, ImmutableList.<PluginProperty>of(), callContext);
         final Account account = accountUserApi.getAccountById(payment.getAccountId(), callContext);
-        final String transactionExternalKey = json.getTransactionExternalKey() != null ? json.getTransactionExternalKey() : UUID.randomUUID().toString();
+        final String transactionExternalKey = json.getTransactionExternalKey() != null ? json.getTransactionExternalKey() : UUIDs.randomUUID().toString();
 
         final Payment result = paymentApi.createChargebackWithPaymentControl(account, payment.getId(), json.getAmount(), account.getCurrency(),
                                                                                    transactionExternalKey, createInvoicePaymentControlPluginApiPaymentOptions(false), callContext);
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/JaxrsResource.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/JaxrsResource.java
index e7a52cf..4c5dabb 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/JaxrsResource.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/JaxrsResource.java
@@ -117,6 +117,9 @@ public interface JaxrsResource {
 
     public static final String PAGINATION = "pagination";
 
+    public static final String ADMIN = "admin";
+    public static final String ADMIN_PATH = PREFIX + "/" + ADMIN;
+
     public static final String ACCOUNTS = "accounts";
     public static final String ACCOUNTS_PATH = PREFIX + "/" + ACCOUNTS;
 
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/JaxRsResourceBase.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/JaxRsResourceBase.java
index 3e41227..06581fc 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/JaxRsResourceBase.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/JaxRsResourceBase.java
@@ -65,6 +65,7 @@ import org.killbill.billing.payment.api.PaymentOptions;
 import org.killbill.billing.payment.api.PaymentTransaction;
 import org.killbill.billing.payment.api.PluginProperty;
 import org.killbill.billing.payment.api.TransactionType;
+import org.killbill.billing.util.UUIDs;
 import org.killbill.billing.util.api.AuditUserApi;
 import org.killbill.billing.util.api.CustomFieldApiException;
 import org.killbill.billing.util.api.CustomFieldUserApi;
@@ -369,8 +370,8 @@ public abstract class JaxRsResourceBase implements JaxrsResource {
             properties.add(pluginPropertyIterator.next());
         }
 
-        final String paymentExternalKey = UUID.randomUUID().toString();
-        final String transactionExternalKey = UUID.randomUUID().toString();
+        final String paymentExternalKey = UUIDs.randomUUID().toString();
+        final String transactionExternalKey = UUIDs.randomUUID().toString();
         final PluginProperty invoiceProperty = new PluginProperty("IPCD_INVOICE_ID" /* InvoicePaymentControlPluginApi.PROP_IPCD_INVOICE_ID (contract with plugin)  */,
                                                                   invoiceId.toString(), false);
         properties.add(invoiceProperty);
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/util/Context.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/util/Context.java
index 1ce9665..b2a67c0 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/util/Context.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/util/Context.java
@@ -16,12 +16,11 @@
 
 package org.killbill.billing.jaxrs.util;
 
-import java.util.UUID;
-
 import javax.servlet.ServletRequest;
 
 import org.killbill.billing.jaxrs.resources.JaxrsResource;
 import org.killbill.billing.tenant.api.Tenant;
+import org.killbill.billing.util.UUIDs;
 import org.killbill.billing.util.callcontext.CallContext;
 import org.killbill.billing.util.callcontext.CallContextFactory;
 import org.killbill.billing.util.callcontext.CallOrigin;
@@ -50,7 +49,7 @@ public class Context {
             Preconditions.checkNotNull(createdBy, String.format("Header %s needs to be set", JaxrsResource.HDR_CREATED_BY));
             final Tenant tenant = getTenantFromRequest(request);
             return contextFactory.createCallContext(tenant == null ? null : tenant.getId(), createdBy, origin, userType, reason,
-                                                    comment, UUID.randomUUID());
+                                                    comment, UUIDs.randomUUID());
         } catch (final NullPointerException e) {
             throw new IllegalArgumentException(e.getMessage());
         }
diff --git a/jaxrs/src/test/java/org/killbill/billing/jaxrs/JaxrsTestUtils.java b/jaxrs/src/test/java/org/killbill/billing/jaxrs/JaxrsTestUtils.java
index c98e3ac..a43c2be 100644
--- a/jaxrs/src/test/java/org/killbill/billing/jaxrs/JaxrsTestUtils.java
+++ b/jaxrs/src/test/java/org/killbill/billing/jaxrs/JaxrsTestUtils.java
@@ -18,19 +18,19 @@ package org.killbill.billing.jaxrs;
 
 import java.util.ArrayList;
 import java.util.List;
-import java.util.UUID;
 
 import org.joda.time.DateTime;
 
 import org.killbill.billing.jaxrs.json.AuditLogJson;
+import org.killbill.billing.util.UUIDs;
 
 public abstract class JaxrsTestUtils {
 
     public static List<AuditLogJson> createAuditLogsJson(final DateTime changeDate) {
         final List<AuditLogJson> auditLogs = new ArrayList<AuditLogJson>();
         for (int i = 0; i < 20; i++) {
-            auditLogs.add(new AuditLogJson(UUID.randomUUID().toString(), changeDate, UUID.randomUUID().toString(),
-                                           UUID.randomUUID().toString(), UUID.randomUUID().toString(), UUID.randomUUID().toString()));
+            auditLogs.add(new AuditLogJson(UUIDs.randomUUID().toString(), changeDate, UUIDs.randomUUID().toString(),
+                                           UUIDs.randomUUID().toString(), UUIDs.randomUUID().toString(), UUIDs.randomUUID().toString()));
         }
 
         return auditLogs;
diff --git a/jaxrs/src/test/java/org/killbill/billing/jaxrs/json/TestInvoiceEmailJson.java b/jaxrs/src/test/java/org/killbill/billing/jaxrs/json/TestInvoiceEmailJson.java
index 91786ba..32a4378 100644
--- a/jaxrs/src/test/java/org/killbill/billing/jaxrs/json/TestInvoiceEmailJson.java
+++ b/jaxrs/src/test/java/org/killbill/billing/jaxrs/json/TestInvoiceEmailJson.java
@@ -22,12 +22,13 @@ import org.testng.Assert;
 import org.testng.annotations.Test;
 
 import org.killbill.billing.jaxrs.JaxrsTestSuiteNoDB;
+import org.killbill.billing.util.UUIDs;
 
 public class TestInvoiceEmailJson extends JaxrsTestSuiteNoDB {
 
     @Test(groups = "fast")
     public void testJson() throws Exception {
-        final String accountId = UUID.randomUUID().toString();
+        final String accountId = UUIDs.randomUUID().toString();
         final boolean isNotifiedForInvoices = true;
 
         final InvoiceEmailJson invoiceEmailJson = new InvoiceEmailJson(accountId, isNotifiedForInvoices);
diff --git a/jaxrs/src/test/java/org/killbill/billing/jaxrs/TestDateConversion.java b/jaxrs/src/test/java/org/killbill/billing/jaxrs/TestDateConversion.java
index e568fea..a09bf3f 100644
--- a/jaxrs/src/test/java/org/killbill/billing/jaxrs/TestDateConversion.java
+++ b/jaxrs/src/test/java/org/killbill/billing/jaxrs/TestDateConversion.java
@@ -23,7 +23,6 @@ import org.joda.time.DateTimeZone;
 import org.joda.time.LocalDate;
 import org.mockito.Mockito;
 import org.testng.Assert;
-import org.testng.annotations.BeforeClass;
 import org.testng.annotations.BeforeTest;
 import org.testng.annotations.Test;
 
@@ -32,6 +31,7 @@ import org.killbill.billing.account.api.AccountApiException;
 import org.killbill.billing.account.api.AccountUserApi;
 import org.killbill.clock.ClockMock;
 import org.killbill.billing.jaxrs.resources.JaxRsResourceBase;
+import org.killbill.billing.util.UUIDs;
 
 public class TestDateConversion extends JaxRsResourceBase {
 
@@ -40,7 +40,7 @@ public class TestDateConversion extends JaxRsResourceBase {
     }
 
     public UUID setupAccount(DateTimeZone accountTimeZone) throws AccountApiException {
-        final UUID accountId = UUID.randomUUID();
+        final UUID accountId = UUIDs.randomUUID();
         final Account account = Mockito.mock(Account.class);
         Mockito.when(account.getTimeZone()).thenReturn(accountTimeZone);
         Mockito.when(accountUserApi.getAccountById(accountId, null)).thenReturn(account);
diff --git a/junction/src/main/java/org/killbill/billing/junction/plumbing/billing/DefaultInternalBillingApi.java b/junction/src/main/java/org/killbill/billing/junction/plumbing/billing/DefaultInternalBillingApi.java
index 4b028a3..84a3050 100644
--- a/junction/src/main/java/org/killbill/billing/junction/plumbing/billing/DefaultInternalBillingApi.java
+++ b/junction/src/main/java/org/killbill/billing/junction/plumbing/billing/DefaultInternalBillingApi.java
@@ -31,7 +31,6 @@ import org.killbill.billing.callcontext.InternalCallContext;
 import org.killbill.billing.catalog.api.CatalogApiException;
 import org.killbill.billing.catalog.api.CatalogService;
 import org.killbill.billing.catalog.api.StaticCatalog;
-import org.killbill.billing.entitlement.EntitlementTransitionType;
 import org.killbill.billing.entitlement.api.SubscriptionEventType;
 import org.killbill.billing.events.EffectiveSubscriptionInternalEvent;
 import org.killbill.billing.invoice.api.DryRunArguments;
@@ -43,6 +42,7 @@ import org.killbill.billing.subscription.api.SubscriptionBaseInternalApi;
 import org.killbill.billing.subscription.api.user.SubscriptionBaseApiException;
 import org.killbill.billing.subscription.api.user.SubscriptionBaseBundle;
 import org.killbill.billing.tag.TagInternalApi;
+import org.killbill.billing.util.UUIDs;
 import org.killbill.billing.util.tag.ControlTagType;
 import org.killbill.billing.util.tag.Tag;
 import org.killbill.clock.Clock;
@@ -133,7 +133,7 @@ public class DefaultInternalBillingApi implements BillingInternalApi {
         if (dryRunArguments != null &&
             dryRunArguments.getAction() == SubscriptionEventType.START_BILLING &&
             dryRunArguments.getBundleId() == null) {
-            final UUID fakeBundleId = UUID.randomUUID();
+            final UUID fakeBundleId = UUIDs.randomUUID();
             final List<SubscriptionBase> subscriptions = subscriptionApi.getSubscriptionsForBundle(fakeBundleId, dryRunArguments, context);
 
             addBillingEventsForSubscription(subscriptions, fakeBundleId, account, dryRunMode, context, result);
diff --git a/payment/src/main/java/org/killbill/billing/payment/api/DefaultAdminPaymentApi.java b/payment/src/main/java/org/killbill/billing/payment/api/DefaultAdminPaymentApi.java
new file mode 100644
index 0000000..a256c61
--- /dev/null
+++ b/payment/src/main/java/org/killbill/billing/payment/api/DefaultAdminPaymentApi.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2014-2015 Groupon, Inc
+ * Copyright 2014-2015 The Billing Project, LLC
+ *
+ * The Billing Project licenses this file to you under the Apache License, version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at:
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.killbill.billing.payment.api;
+
+import java.util.UUID;
+
+import javax.annotation.Nullable;
+import javax.inject.Inject;
+
+import org.killbill.billing.callcontext.InternalCallContext;
+import org.killbill.billing.payment.dao.PaymentDao;
+import org.killbill.billing.util.callcontext.CallContext;
+import org.killbill.billing.util.callcontext.InternalCallContextFactory;
+import org.killbill.commons.locker.GlobalLocker;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class DefaultAdminPaymentApi implements AdminPaymentApi {
+
+    private static final Logger log = LoggerFactory.getLogger(DefaultAdminPaymentApi.class);
+
+    private final PaymentDao paymentDao;
+    private final GlobalLocker locker;
+    private final InternalCallContextFactory internalCallContextFactory;
+
+    @Inject
+    public DefaultAdminPaymentApi(final PaymentDao paymentDao, final InternalCallContextFactory internalCallContextFactory, final GlobalLocker locker) {
+        this.paymentDao = paymentDao;
+        this.internalCallContextFactory = internalCallContextFactory;
+        this.locker = locker;
+    }
+
+    @Override
+    public void fixPaymentTransactionState(final Payment payment, PaymentTransaction paymentTransaction, TransactionStatus transactionStatus, @Nullable String lastSuccessPaymentState, String currentPaymentStateName,
+                                              Iterable<PluginProperty> properties, CallContext callContext)
+            throws PaymentApiException {
+
+        final InternalCallContext internalCallContext = internalCallContextFactory.createInternalCallContext(payment.getAccountId(), callContext);
+        paymentDao.updatePaymentAndTransactionOnCompletion(payment.getAccountId(), payment.getId(), paymentTransaction.getTransactionType(),
+                                                       currentPaymentStateName, lastSuccessPaymentState, paymentTransaction.getId(),
+                                                       transactionStatus, paymentTransaction.getProcessedAmount(), paymentTransaction.getProcessedCurrency(),
+                                                       paymentTransaction.getGatewayErrorCode(), paymentTransaction.getGatewayErrorMsg(), 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 2bb3816..23fe159 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
@@ -33,6 +33,7 @@ import org.killbill.billing.payment.core.PaymentMethodProcessor;
 import org.killbill.billing.payment.core.PaymentProcessor;
 import org.killbill.billing.payment.core.PluginRoutingPaymentProcessor;
 import org.killbill.billing.payment.invoice.InvoicePaymentRoutingPluginApi;
+import org.killbill.billing.util.UUIDs;
 import org.killbill.billing.util.callcontext.CallContext;
 import org.killbill.billing.util.callcontext.InternalCallContextFactory;
 import org.killbill.billing.util.callcontext.TenantContext;
@@ -84,7 +85,7 @@ public class DefaultPaymentApi implements PaymentApi {
     }
 
     @Override
-    public Payment createAuthorizationWithPaymentControl(final Account account, final UUID paymentMethodId, final UUID paymentId, final BigDecimal amount, final Currency currency,
+    public Payment createAuthorizationWithPaymentControl(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 PaymentOptions paymentOptions, final CallContext callContext) throws PaymentApiException {
         final List<String> paymentControlPluginNames = toPaymentControlPluginNames(paymentOptions);
@@ -124,6 +125,25 @@ public class DefaultPaymentApi implements PaymentApi {
     }
 
     @Override
+    public Payment createCaptureWithPaymentControl(final Account account, final UUID paymentId, final BigDecimal amount, final Currency currency, @Nullable final String paymentTransactionExternalKey,
+                                                   final Iterable<PluginProperty> properties, final PaymentOptions paymentOptions, final CallContext callContext) throws PaymentApiException {
+        final List<String> paymentControlPluginNames = toPaymentControlPluginNames(paymentOptions);
+        if (paymentControlPluginNames.isEmpty()) {
+            return createCapture(account, paymentId, amount, currency, paymentTransactionExternalKey, properties, callContext);
+        }
+
+        checkNotNullParameter(account, "account");
+        checkNotNullParameter(paymentId, "paymentId");
+        checkNotNullParameter(currency, "currency");
+        checkNotNullParameter(properties, "plugin properties");
+        checkPositiveAmount(amount);
+
+        final InternalCallContext internalCallContext = internalCallContextFactory.createInternalCallContext(account.getId(), callContext);
+        return pluginRoutingPaymentProcessor.createCapture(IS_API_PAYMENT, account, paymentId, amount, currency, paymentTransactionExternalKey,
+                                              properties, paymentControlPluginNames, 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 {
         checkNotNullParameter(account, "account");
@@ -166,7 +186,7 @@ public class DefaultPaymentApi implements PaymentApi {
 
         final UUID nonNulPaymentMethodId = (paymentMethodId != null) ?
                                            paymentMethodId :
-                                           paymentMethodProcessor.createOrGetExternalPaymentMethod(UUID.randomUUID().toString(), account, properties, callContext, internalCallContext);
+                                           paymentMethodProcessor.createOrGetExternalPaymentMethod(UUIDs.randomUUID().toString(), account, properties, callContext, internalCallContext);
         return pluginRoutingPaymentProcessor.createPurchase(IS_API_PAYMENT, account, nonNulPaymentMethodId, paymentId, amount, currency, paymentExternalKey, paymentTransactionExternalKey,
                                                             properties, paymentControlPluginNames, callContext, internalCallContext);
 
@@ -189,6 +209,24 @@ public class DefaultPaymentApi implements PaymentApi {
     }
 
     @Override
+    public Payment createVoidWithPaymentControl(final Account account, final UUID paymentId, final String paymentTransactionExternalKey, final Iterable<PluginProperty> properties, final PaymentOptions paymentOptions, final CallContext callContext) throws PaymentApiException {
+        final List<String> paymentControlPluginNames = toPaymentControlPluginNames(paymentOptions);
+        if (paymentControlPluginNames.isEmpty()) {
+            return createVoid(account, paymentId, paymentTransactionExternalKey, properties, callContext);
+        }
+
+        checkNotNullParameter(account, "account");
+        checkNotNullParameter(paymentId, "paymentId");
+        checkNotNullParameter(properties, "plugin properties");
+
+        logAPICall(TransactionType.VOID.name(), account, null, paymentId, null, null, null, null, paymentTransactionExternalKey);
+
+        final InternalCallContext internalCallContext = internalCallContextFactory.createInternalCallContext(account.getId(), callContext);
+        return pluginRoutingPaymentProcessor.createVoid(IS_API_PAYMENT, account, paymentId, paymentTransactionExternalKey,
+                                           properties, paymentControlPluginNames, 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 {
 
@@ -285,7 +323,7 @@ public class DefaultPaymentApi implements PaymentApi {
     }
 
     @Override
-    public Payment notifyPendingTransactionOfStateChangedWithPaymentControl(final Account account, final UUID paymentTransactionId, final boolean isSuccess, final PaymentOptions paymentOptions, final CallContext context) throws PaymentApiException {
+    public Payment notifyPendingTransactionOfStateChangedWithPaymentControl(final Account account, final UUID paymentTransactionId, final boolean isSuccess, final PaymentOptions paymentOptions, final CallContext callContext) throws PaymentApiException {
         throw new IllegalStateException("Not implemented");
     }
 
@@ -319,7 +357,7 @@ public class DefaultPaymentApi implements PaymentApi {
         checkPositiveAmount(amount);
 
         final InternalCallContext internalCallContext = internalCallContextFactory.createInternalCallContext(account.getId(), callContext);
-        return pluginRoutingPaymentProcessor.createChargeback(account, paymentId, paymentTransactionExternalKey, amount, currency,
+        return pluginRoutingPaymentProcessor.createChargeback(IS_API_PAYMENT, account, paymentId, paymentTransactionExternalKey, amount, currency,
                                                               paymentControlPluginNames, callContext, internalCallContext);
     }
 
diff --git a/payment/src/main/java/org/killbill/billing/payment/api/DefaultPaymentGatewayApi.java b/payment/src/main/java/org/killbill/billing/payment/api/DefaultPaymentGatewayApi.java
index 526b9db..c9adbaf 100644
--- a/payment/src/main/java/org/killbill/billing/payment/api/DefaultPaymentGatewayApi.java
+++ b/payment/src/main/java/org/killbill/billing/payment/api/DefaultPaymentGatewayApi.java
@@ -53,7 +53,17 @@ public class DefaultPaymentGatewayApi implements PaymentGatewayApi {
     }
 
     @Override
+    public HostedPaymentPageFormDescriptor buildFormDescriptorWithPaymentControl(final Account account, final UUID uuid, final Iterable<PluginProperty> iterable, final Iterable<PluginProperty> iterable1, final PaymentOptions paymentOptions, final CallContext callContext) throws PaymentApiException {
+        throw new IllegalStateException("Not implemented");
+    }
+
+    @Override
     public GatewayNotification processNotification(final String notification, final String pluginName, final Iterable<PluginProperty> properties, final CallContext callContext) throws PaymentApiException {
         return paymentGatewayProcessor.processNotification(notification, pluginName, properties, callContext);
     }
+
+    @Override
+    public GatewayNotification processNotificationWithPaymentControl(final String s, final String s1, final Iterable<PluginProperty> iterable, final PaymentOptions paymentOptions, final CallContext callContext) throws PaymentApiException {
+        throw new IllegalStateException("Not implemented");
+    }
 }
diff --git a/payment/src/main/java/org/killbill/billing/payment/api/DefaultPaymentMethod.java b/payment/src/main/java/org/killbill/billing/payment/api/DefaultPaymentMethod.java
index e414f61..b897b63 100644
--- a/payment/src/main/java/org/killbill/billing/payment/api/DefaultPaymentMethod.java
+++ b/payment/src/main/java/org/killbill/billing/payment/api/DefaultPaymentMethod.java
@@ -24,6 +24,7 @@ import org.joda.time.DateTime;
 
 import org.killbill.billing.payment.dao.PaymentMethodModelDao;
 import org.killbill.billing.entity.EntityBase;
+import org.killbill.billing.util.UUIDs;
 
 public class DefaultPaymentMethod extends EntityBase implements PaymentMethod {
 
@@ -44,7 +45,7 @@ public class DefaultPaymentMethod extends EntityBase implements PaymentMethod {
     }
 
     public DefaultPaymentMethod(final String externalKey, final UUID accountId, final String pluginName, final PaymentMethodPlugin pluginDetail) {
-        this(UUID.randomUUID(), externalKey, null, null, accountId, true, pluginName, pluginDetail);
+        this(UUIDs.randomUUID(), externalKey, null, null, accountId, true, pluginName, pluginDetail);
     }
 
     public DefaultPaymentMethod(final UUID paymentMethodId, final String externalKey, final UUID accountId, final String pluginName) {
diff --git a/payment/src/main/java/org/killbill/billing/payment/bus/InvoiceHandler.java b/payment/src/main/java/org/killbill/billing/payment/bus/InvoiceHandler.java
index 22dd22c..f6ff6ff 100644
--- a/payment/src/main/java/org/killbill/billing/payment/bus/InvoiceHandler.java
+++ b/payment/src/main/java/org/killbill/billing/payment/bus/InvoiceHandler.java
@@ -22,7 +22,6 @@ import java.math.BigDecimal;
 import java.util.ArrayList;
 import java.util.LinkedList;
 import java.util.List;
-import java.util.UUID;
 
 import org.killbill.billing.ErrorCode;
 import org.killbill.billing.account.api.Account;
@@ -34,6 +33,7 @@ import org.killbill.billing.payment.api.PaymentApiException;
 import org.killbill.billing.payment.api.PluginProperty;
 import org.killbill.billing.payment.core.PluginRoutingPaymentProcessor;
 import org.killbill.billing.payment.invoice.InvoicePaymentRoutingPluginApi;
+import org.killbill.billing.util.UUIDs;
 import org.killbill.billing.util.callcontext.CallContext;
 import org.killbill.billing.util.callcontext.CallOrigin;
 import org.killbill.billing.util.callcontext.InternalCallContextFactory;
@@ -84,7 +84,7 @@ public class InvoiceHandler {
             final BigDecimal amountToBePaid = null; // We let the plugin compute how much should be paid
             final List<String> paymentControlPluginNames = paymentConfig.getPaymentControlPluginNames() != null ? new LinkedList<String>(paymentConfig.getPaymentControlPluginNames()) : new LinkedList<String>();
             paymentControlPluginNames.add(InvoicePaymentRoutingPluginApi.PLUGIN_NAME);
-            pluginRoutingPaymentProcessor.createPurchase(false, account, account.getPaymentMethodId(), null, amountToBePaid, account.getCurrency(), UUID.randomUUID().toString(), UUID.randomUUID().toString(),
+            pluginRoutingPaymentProcessor.createPurchase(false, account, account.getPaymentMethodId(), null, amountToBePaid, account.getCurrency(), UUIDs.randomUUID().toString(), UUIDs.randomUUID().toString(),
                                                          properties, paymentControlPluginNames, callContext, internalContext);
         } catch (final AccountApiException e) {
             log.error("Failed to process invoice payment", e);
diff --git a/payment/src/main/java/org/killbill/billing/payment/core/janitor/CompletionTaskBase.java b/payment/src/main/java/org/killbill/billing/payment/core/janitor/CompletionTaskBase.java
index 623ab72..1073e71 100644
--- a/payment/src/main/java/org/killbill/billing/payment/core/janitor/CompletionTaskBase.java
+++ b/payment/src/main/java/org/killbill/billing/payment/core/janitor/CompletionTaskBase.java
@@ -18,19 +18,18 @@
 package org.killbill.billing.payment.core.janitor;
 
 import java.util.List;
-import java.util.UUID;
 
 import org.joda.time.DateTime;
 import org.killbill.billing.account.api.AccountInternalApi;
 import org.killbill.billing.callcontext.DefaultCallContext;
-import org.killbill.billing.callcontext.InternalCallContext;
 import org.killbill.billing.callcontext.InternalTenantContext;
 import org.killbill.billing.osgi.api.OSGIServiceRegistration;
 import org.killbill.billing.payment.core.sm.PaymentStateMachineHelper;
 import org.killbill.billing.payment.core.sm.PluginRoutingPaymentAutomatonRunner;
-import org.killbill.billing.payment.core.sm.RetryStateMachineHelper;
+import org.killbill.billing.payment.core.sm.PaymentControlStateMachineHelper;
 import org.killbill.billing.payment.dao.PaymentDao;
 import org.killbill.billing.payment.plugin.api.PaymentPluginApi;
+import org.killbill.billing.util.UUIDs;
 import org.killbill.billing.util.callcontext.CallContext;
 import org.killbill.billing.util.callcontext.CallOrigin;
 import org.killbill.billing.util.callcontext.InternalCallContextFactory;
@@ -51,17 +50,16 @@ abstract class CompletionTaskBase<T> implements Runnable {
     protected final PaymentConfig paymentConfig;
     protected final Clock clock;
     protected final PaymentDao paymentDao;
-    protected final InternalCallContext completionTaskCallContext;
     protected final InternalCallContextFactory internalCallContextFactory;
     protected final PaymentStateMachineHelper paymentStateMachineHelper;
-    protected final RetryStateMachineHelper retrySMHelper;
+    protected final PaymentControlStateMachineHelper retrySMHelper;
     protected final AccountInternalApi accountInternalApi;
     protected final PluginRoutingPaymentAutomatonRunner pluginControlledPaymentAutomatonRunner;
     protected final OSGIServiceRegistration<PaymentPluginApi> pluginRegistry;
 
     public CompletionTaskBase(final Janitor janitor, final InternalCallContextFactory internalCallContextFactory, final PaymentConfig paymentConfig,
                               final PaymentDao paymentDao, final Clock clock, final PaymentStateMachineHelper paymentStateMachineHelper,
-                              final RetryStateMachineHelper retrySMHelper, final AccountInternalApi accountInternalApi,
+                              final PaymentControlStateMachineHelper retrySMHelper, final AccountInternalApi accountInternalApi,
                               final PluginRoutingPaymentAutomatonRunner pluginControlledPaymentAutomatonRunner, final OSGIServiceRegistration<PaymentPluginApi> pluginRegistry) {
         this.janitor = janitor;
         this.internalCallContextFactory = internalCallContextFactory;
@@ -75,7 +73,6 @@ abstract class CompletionTaskBase<T> implements Runnable {
         this.pluginRegistry = pluginRegistry;
         // Limit the length of the username in the context (limited to 50 characters)
         this.taskName = this.getClass().getSimpleName();
-        this.completionTaskCallContext = internalCallContextFactory.createInternalCallContext((Long) null, (Long) null, taskName, CallOrigin.INTERNAL, UserType.SYSTEM, UUID.randomUUID());
     }
 
     @Override
@@ -105,12 +102,8 @@ abstract class CompletionTaskBase<T> implements Runnable {
 
     protected CallContext createCallContext(final String taskName, final InternalTenantContext internalTenantContext) {
         final TenantContext tenantContext = internalCallContextFactory.createTenantContext(internalTenantContext);
-        final CallContext callContext = new DefaultCallContext(tenantContext.getTenantId(), taskName, CallOrigin.INTERNAL, UserType.SYSTEM, UUID.randomUUID(), clock);
+        final CallContext callContext = new DefaultCallContext(tenantContext.getTenantId(), taskName, CallOrigin.INTERNAL, UserType.SYSTEM, UUIDs.randomUUID(), clock);
         return callContext;
     }
 
-    protected DateTime getCreatedDateBefore() {
-        final long delayBeforeNowMs = paymentConfig.getJanitorPendingCleanupTime().getMillis();
-        return clock.getUTCNow().minusMillis((int) delayBeforeNowMs);
-    }
 }
diff --git a/payment/src/main/java/org/killbill/billing/payment/core/janitor/IncompletePaymentTransactionTask.java b/payment/src/main/java/org/killbill/billing/payment/core/janitor/IncompletePaymentTransactionTask.java
new file mode 100644
index 0000000..4edf27f
--- /dev/null
+++ b/payment/src/main/java/org/killbill/billing/payment/core/janitor/IncompletePaymentTransactionTask.java
@@ -0,0 +1,181 @@
+/*
+ * Copyright 2014-2015 Groupon, Inc
+ * Copyright 2014-2015 The Billing Project, LLC
+ *
+ * The Billing Project licenses this file to you under the Apache License, version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at:
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.killbill.billing.payment.core.janitor;
+
+import java.math.BigDecimal;
+import java.util.List;
+
+import javax.annotation.Nullable;
+
+import org.joda.time.DateTime;
+import org.killbill.billing.account.api.AccountInternalApi;
+import org.killbill.billing.callcontext.InternalCallContext;
+import org.killbill.billing.callcontext.InternalTenantContext;
+import org.killbill.billing.catalog.api.Currency;
+import org.killbill.billing.osgi.api.OSGIServiceRegistration;
+import org.killbill.billing.payment.api.PluginProperty;
+import org.killbill.billing.payment.api.TransactionStatus;
+import org.killbill.billing.payment.core.PaymentTransactionInfoPluginConverter;
+import org.killbill.billing.payment.core.sm.PaymentStateMachineHelper;
+import org.killbill.billing.payment.core.sm.PluginRoutingPaymentAutomatonRunner;
+import org.killbill.billing.payment.core.sm.PaymentControlStateMachineHelper;
+import org.killbill.billing.payment.dao.PaymentDao;
+import org.killbill.billing.payment.dao.PaymentMethodModelDao;
+import org.killbill.billing.payment.dao.PaymentModelDao;
+import org.killbill.billing.payment.dao.PaymentTransactionModelDao;
+import org.killbill.billing.payment.plugin.api.PaymentPluginApi;
+import org.killbill.billing.payment.plugin.api.PaymentPluginApiException;
+import org.killbill.billing.payment.plugin.api.PaymentTransactionInfoPlugin;
+import org.killbill.billing.util.callcontext.CallContext;
+import org.killbill.billing.util.callcontext.InternalCallContextFactory;
+import org.killbill.billing.util.config.PaymentConfig;
+import org.killbill.clock.Clock;
+
+import com.google.common.base.Preconditions;
+import com.google.common.base.Predicate;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
+
+public class IncompletePaymentTransactionTask extends CompletionTaskBase<PaymentTransactionModelDao> {
+
+    private final static ImmutableList<TransactionStatus> TRANSACTION_STATUSES_TO_CONSIDER = ImmutableList.<TransactionStatus>builder()
+                                                                                                          .add(TransactionStatus.PENDING)
+                                                                                                          .add(TransactionStatus.UNKNOWN)
+                                                                                                          .build();
+    private final static int MAX_ITEMS_PER_LOOP = 100;
+
+
+    public IncompletePaymentTransactionTask(final Janitor janitor, final InternalCallContextFactory internalCallContextFactory, final PaymentConfig paymentConfig,
+                                            final PaymentDao paymentDao, final Clock clock,
+                                            final PaymentStateMachineHelper paymentStateMachineHelper, final PaymentControlStateMachineHelper retrySMHelper, final AccountInternalApi accountInternalApi,
+                                            final PluginRoutingPaymentAutomatonRunner pluginControlledPaymentAutomatonRunner, final OSGIServiceRegistration<PaymentPluginApi> pluginRegistry) {
+        super(janitor, internalCallContextFactory, paymentConfig, paymentDao, clock, paymentStateMachineHelper, retrySMHelper, accountInternalApi, pluginControlledPaymentAutomatonRunner, pluginRegistry);
+    }
+
+    @Override
+    public List<PaymentTransactionModelDao> getItemsForIteration() {
+        final List<PaymentTransactionModelDao> result = paymentDao.getByTransactionStatusAcrossTenants(TRANSACTION_STATUSES_TO_CONSIDER, getCreatedDateBefore(), getCreatedDateAfter(), MAX_ITEMS_PER_LOOP);
+        if (!result.isEmpty()) {
+            log.info("Janitor IncompletePaymentTransactionTask start run: found {} errored/unknown payments", result.size());
+        }
+        return result;
+    }
+
+    @Override
+    public void doIteration(final PaymentTransactionModelDao paymentTransaction) {
+
+        final InternalTenantContext internalTenantContext = internalCallContextFactory.createInternalTenantContext(paymentTransaction.getTenantRecordId(), paymentTransaction.getAccountRecordId());
+        final CallContext callContext = createCallContext("IncompletePaymentTransactionTask", internalTenantContext);
+        final PaymentModelDao payment = paymentDao.getPayment(paymentTransaction.getPaymentId(), internalTenantContext);
+
+
+        final PaymentMethodModelDao paymentMethod = paymentDao.getPaymentMethod(payment.getPaymentMethodId(), internalTenantContext);
+        final PaymentPluginApi paymentPluginApi = getPaymentPluginApi(payment, paymentMethod.getPluginName());
+
+        PaymentTransactionInfoPlugin paymentTransactionInfoPlugin;
+        try {
+            final List<PaymentTransactionInfoPlugin> result = paymentPluginApi.getPaymentInfo(payment.getAccountId(), payment.getId(), ImmutableList.<PluginProperty>of(), callContext);
+            paymentTransactionInfoPlugin = Iterables.tryFind(result, new Predicate<PaymentTransactionInfoPlugin>() {
+                @Override
+                public boolean apply(final PaymentTransactionInfoPlugin input) {
+                    return input.getKbTransactionPaymentId().equals(paymentTransaction.getId());
+                }
+            }).orNull();
+        } catch (final PaymentPluginApiException ignored) {
+            // The paymentTransactionInfoPlugin will be null and handled below as if the plugin did not know about that transaction
+            paymentTransactionInfoPlugin = null;
+        }
+
+        //
+        // First obtain the new transactionStatus,
+        // Then compute the new paymentState; this one is mostly interesting in case of success (to compute the lastSuccessPaymentState below)
+        //
+        final TransactionStatus transactionStatus = computeNewTransactionStatusFromPaymentTransactionInfoPlugin(paymentTransactionInfoPlugin, paymentTransaction.getTransactionStatus());
+        final String newPaymentState;
+        switch (transactionStatus) {
+            case PENDING:
+                newPaymentState = paymentStateMachineHelper.getPendingStateForTransaction(paymentTransaction.getTransactionType());
+                break;
+            case SUCCESS:
+                newPaymentState = paymentStateMachineHelper.getSuccessfulStateForTransaction(paymentTransaction.getTransactionType());
+                break;
+            case PAYMENT_FAILURE:
+                newPaymentState = paymentStateMachineHelper.getFailureStateForTransaction(paymentTransaction.getTransactionType());
+                break;
+            case PLUGIN_FAILURE:
+            case UNKNOWN:
+            default:
+                // In this case we don't try to update the current paymentState (since we could not get anything interesting from the plugin)
+                newPaymentState = payment.getStateName();
+                break;
+        }
+
+        // Recompute new lastSuccessPaymentState. This is important to be able to allow new operations on the state machine (for e.g an AUTH_SUCCESS would now allow a CAPTURE operation)
+        final String lastSuccessPaymentState = paymentStateMachineHelper.isSuccessState(newPaymentState) ? newPaymentState : null;
+
+        // Update the processedAmount, processedCurrency if we got a paymentTransactionInfoPlugin from the plugin and if this is a non error state
+        final BigDecimal processedAmount = (paymentTransactionInfoPlugin != null && isPendingOrFinalTransactionStatus(transactionStatus)) ?
+                                           paymentTransactionInfoPlugin.getAmount() : paymentTransaction.getProcessedAmount();
+        final Currency processedCurrency = (paymentTransactionInfoPlugin != null && isPendingOrFinalTransactionStatus(transactionStatus)) ?
+                                           paymentTransactionInfoPlugin.getCurrency() : paymentTransaction.getProcessedCurrency();
+
+        // Update the gatewayErrorCode, gatewayError if we got a paymentTransactionInfoPlugin
+        final String gatewayErrorCode = paymentTransactionInfoPlugin != null ? paymentTransactionInfoPlugin.getGatewayErrorCode() : paymentTransaction.getGatewayErrorCode();
+        final String gatewayError = paymentTransactionInfoPlugin != null ? paymentTransactionInfoPlugin.getGatewayError() : paymentTransaction.getGatewayErrorMsg();
+
+        log.info("Janitor IncompletePaymentTransactionTask repairing payment {}, transaction {}", payment.getId(), paymentTransaction.getId());
+
+        final InternalCallContext internalCallContext = internalCallContextFactory.createInternalCallContext(payment.getAccountId(), callContext);
+        paymentDao.updatePaymentAndTransactionOnCompletion(payment.getAccountId(), payment.getId(), paymentTransaction.getTransactionType(), newPaymentState, lastSuccessPaymentState,
+                                                           paymentTransaction.getId(), transactionStatus, processedAmount, processedCurrency, gatewayErrorCode, gatewayError, internalCallContext);
+
+    }
+
+    // Keep the existing currentTransactionStatus if we can't obtain a better answer from the plugin; if not, return the newTransactionStatus
+    private TransactionStatus computeNewTransactionStatusFromPaymentTransactionInfoPlugin(@Nullable PaymentTransactionInfoPlugin input, final TransactionStatus currentTransactionStatus) {
+        final TransactionStatus newTransactionStatus = PaymentTransactionInfoPluginConverter.toTransactionStatus(input);
+        if (newTransactionStatus == TransactionStatus.PLUGIN_FAILURE || newTransactionStatus ==  TransactionStatus.UNKNOWN) {
+            return currentTransactionStatus;
+        } else {
+            return newTransactionStatus;
+        }
+    }
+
+    private boolean isPendingOrFinalTransactionStatus(final TransactionStatus transactionStatus) {
+        return (transactionStatus == TransactionStatus.PENDING ||
+                transactionStatus == TransactionStatus.SUCCESS ||
+                transactionStatus == TransactionStatus.PAYMENT_FAILURE);
+    }
+
+    private PaymentPluginApi getPaymentPluginApi(final PaymentModelDao item, final String pluginName) {
+        final PaymentPluginApi pluginApi = pluginRegistry.getServiceForName(pluginName);
+        Preconditions.checkState(pluginApi != null, "Janitor IncompletePaymentTransactionTask cannot retrieve PaymentPluginApi " + item.getId() + ", skipping");
+        return pluginApi;
+    }
+
+    private DateTime getCreatedDateBefore() {
+        final long delayBeforeNowMs = paymentConfig.getIncompleteTransactionsTimeSpanDelay().getMillis();
+        return clock.getUTCNow().minusMillis((int) delayBeforeNowMs);
+    }
+
+
+    private DateTime getCreatedDateAfter() {
+        final long delayBeforeNowMs = paymentConfig.getIncompleteTransactionsTimeSpanGiveup().getMillis();
+        return clock.getUTCNow().minusMillis((int) delayBeforeNowMs);
+    }
+}
diff --git a/payment/src/main/java/org/killbill/billing/payment/core/janitor/Janitor.java b/payment/src/main/java/org/killbill/billing/payment/core/janitor/Janitor.java
index cd39edc..73eddf5 100644
--- a/payment/src/main/java/org/killbill/billing/payment/core/janitor/Janitor.java
+++ b/payment/src/main/java/org/killbill/billing/payment/core/janitor/Janitor.java
@@ -27,7 +27,7 @@ import org.killbill.billing.account.api.AccountInternalApi;
 import org.killbill.billing.osgi.api.OSGIServiceRegistration;
 import org.killbill.billing.payment.core.sm.PaymentStateMachineHelper;
 import org.killbill.billing.payment.core.sm.PluginRoutingPaymentAutomatonRunner;
-import org.killbill.billing.payment.core.sm.RetryStateMachineHelper;
+import org.killbill.billing.payment.core.sm.PaymentControlStateMachineHelper;
 import org.killbill.billing.payment.dao.PaymentDao;
 import org.killbill.billing.payment.glue.PaymentModule;
 import org.killbill.billing.payment.plugin.api.PaymentPluginApi;
@@ -48,9 +48,8 @@ public class Janitor {
 
     private final ScheduledExecutorService janitorExecutor;
     private final PaymentConfig paymentConfig;
-    private final PendingTransactionTask pendingTransactionTask;
-    private final AttemptCompletionTask attemptCompletionTask;
-    private final ErroredPaymentTask erroredPaymentCompletionTask;
+    private final IncompletePaymentAttemptTask incompletePaymentAttemptTask;
+    private final IncompletePaymentTransactionTask incompletePaymentTransactionTask;
 
     private volatile boolean isStopped;
 
@@ -63,15 +62,13 @@ public class Janitor {
                    final PluginRoutingPaymentAutomatonRunner pluginControlledPaymentAutomatonRunner,
                    @Named(PaymentModule.JANITOR_EXECUTOR_NAMED) final ScheduledExecutorService janitorExecutor,
                    final PaymentStateMachineHelper paymentSMHelper,
-                   final RetryStateMachineHelper retrySMHelper,
+                   final PaymentControlStateMachineHelper retrySMHelper,
                    final OSGIServiceRegistration<PaymentPluginApi> pluginRegistry) {
         this.janitorExecutor = janitorExecutor;
         this.paymentConfig = paymentConfig;
-        this.pendingTransactionTask = new PendingTransactionTask(this, internalCallContextFactory, paymentConfig, paymentDao, clock, paymentSMHelper, retrySMHelper,
-                                                                 accountInternalApi, pluginControlledPaymentAutomatonRunner, pluginRegistry);
-        this.attemptCompletionTask = new AttemptCompletionTask(this, internalCallContextFactory, paymentConfig, paymentDao, clock, paymentSMHelper, retrySMHelper,
+        this.incompletePaymentAttemptTask = new IncompletePaymentAttemptTask(this, internalCallContextFactory, paymentConfig, paymentDao, clock, paymentSMHelper, retrySMHelper,
                                                                accountInternalApi, pluginControlledPaymentAutomatonRunner, pluginRegistry);
-        this.erroredPaymentCompletionTask = new ErroredPaymentTask(this, internalCallContextFactory, paymentConfig, paymentDao, clock, paymentSMHelper, retrySMHelper,
+        this.incompletePaymentTransactionTask = new IncompletePaymentTransactionTask(this, internalCallContextFactory, paymentConfig, paymentDao, clock, paymentSMHelper, retrySMHelper,
                                                                    accountInternalApi, pluginControlledPaymentAutomatonRunner, pluginRegistry);
         this.isStopped = false;
     }
@@ -82,20 +79,15 @@ public class Janitor {
             return;
         }
 
-        // Start task for removing old pending payments.
-        final TimeUnit pendingRateUnit = paymentConfig.getJanitorRunningRate().getUnit();
-        final long pendingPeriod = paymentConfig.getJanitorRunningRate().getPeriod();
-        janitorExecutor.scheduleAtFixedRate(pendingTransactionTask, pendingPeriod, pendingPeriod, pendingRateUnit);
-
         // Start task for completing incomplete payment attempts
         final TimeUnit attemptCompletionRateUnit = paymentConfig.getJanitorRunningRate().getUnit();
         final long attemptCompletionPeriod = paymentConfig.getJanitorRunningRate().getPeriod();
-        janitorExecutor.scheduleAtFixedRate(attemptCompletionTask, attemptCompletionPeriod, attemptCompletionPeriod, attemptCompletionRateUnit);
+        janitorExecutor.scheduleAtFixedRate(incompletePaymentAttemptTask, attemptCompletionPeriod, attemptCompletionPeriod, attemptCompletionRateUnit);
 
         // Start task for completing incomplete payment attempts
         final TimeUnit erroredCompletionRateUnit = paymentConfig.getJanitorRunningRate().getUnit();
         final long erroredCompletionPeriod = paymentConfig.getJanitorRunningRate().getPeriod();
-        janitorExecutor.scheduleAtFixedRate(erroredPaymentCompletionTask, erroredCompletionPeriod, erroredCompletionPeriod, erroredCompletionRateUnit);
+        janitorExecutor.scheduleAtFixedRate(incompletePaymentTransactionTask, erroredCompletionPeriod, erroredCompletionPeriod, erroredCompletionRateUnit);
     }
 
     public void stop() {
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 d179396..4babea1 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
@@ -72,6 +72,7 @@ import com.google.inject.Inject;
 import com.google.inject.name.Named;
 
 import static org.killbill.billing.payment.glue.PaymentModule.PLUGIN_EXECUTOR_NAMED;
+import org.killbill.billing.util.UUIDs;
 import static org.killbill.billing.util.entity.dao.DefaultPaginationHelper.getEntityPagination;
 import static org.killbill.billing.util.entity.dao.DefaultPaginationHelper.getEntityPaginationFromPlugins;
 
@@ -307,7 +308,7 @@ public class PaymentMethodProcessor extends ProcessorBase {
         if (externalPaymentMethod != null) {
             return externalPaymentMethod.getId();
         }
-        final DefaultNoOpPaymentMethodPlugin props = new DefaultNoOpPaymentMethodPlugin(UUID.randomUUID().toString(), false, properties);
+        final DefaultNoOpPaymentMethodPlugin props = new DefaultNoOpPaymentMethodPlugin(UUIDs.randomUUID().toString(), false, properties);
         return addPaymentMethod(paymentMethodExternalKey, ExternalPaymentProviderPlugin.PLUGIN_NAME, account, false, props, properties, callContext, context);
     }
 
@@ -450,7 +451,7 @@ public class PaymentMethodProcessor extends ProcessorBase {
                     final List<PaymentMethodModelDao> finalPaymentMethods = new ArrayList<PaymentMethodModelDao>();
                     for (final PaymentMethodInfoPlugin cur : pluginPms) {
                         // If the kbPaymentId is NULL, the plugin does not know about it, so we create a new UUID
-                        final UUID paymentMethodId = cur.getPaymentMethodId() != null ? cur.getPaymentMethodId() : UUID.randomUUID();
+                        final UUID paymentMethodId = cur.getPaymentMethodId() != null ? cur.getPaymentMethodId() : UUIDs.randomUUID();
                         // TODO paymentMethod externalKey seems broken here.
                         final PaymentMethod input = new DefaultPaymentMethod(paymentMethodId, paymentMethodId.toString(), account.getId(), pluginName);
                         final PaymentMethodModelDao pmModel = new PaymentMethodModelDao(input.getId(), input.getExternalKey(), input.getCreatedDate(), input.getUpdatedDate(),
diff --git a/payment/src/main/java/org/killbill/billing/payment/core/PaymentTransactionInfoPluginConverter.java b/payment/src/main/java/org/killbill/billing/payment/core/PaymentTransactionInfoPluginConverter.java
new file mode 100644
index 0000000..d60d540
--- /dev/null
+++ b/payment/src/main/java/org/killbill/billing/payment/core/PaymentTransactionInfoPluginConverter.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright 2014-2015 Groupon, Inc
+ * Copyright 2014-2015 The Billing Project, LLC
+ *
+ * The Billing Project licenses this file to you under the Apache License, version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at:
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.killbill.billing.payment.core;
+
+import javax.annotation.Nullable;
+
+import org.killbill.automaton.OperationResult;
+import org.killbill.billing.payment.api.TransactionStatus;
+import org.killbill.billing.payment.plugin.api.PaymentTransactionInfoPlugin;
+
+//
+// Conversion between the plugin result to the payment state and transaction status
+//
+public class PaymentTransactionInfoPluginConverter {
+
+    public static TransactionStatus toTransactionStatus(@Nullable final PaymentTransactionInfoPlugin paymentTransactionInfoPlugin) {
+        //
+        // paymentTransactionInfoPlugin when we got an exception from the plugin, or if the plugin behaves badly
+        // and decides to return null; in all cases this is seen as a PLUGIN_FAILURE
+        //
+        if (paymentTransactionInfoPlugin == null || paymentTransactionInfoPlugin.getStatus() == null) {
+            return TransactionStatus.PLUGIN_FAILURE;
+        }
+
+        switch (paymentTransactionInfoPlugin.getStatus()) {
+            case PROCESSED:
+                return TransactionStatus.SUCCESS;
+            case PENDING:
+                return TransactionStatus.PENDING;
+            case ERROR:
+                return TransactionStatus.PAYMENT_FAILURE;
+            // This will be picked up by Janitor to figure out what really happened and correct the state if needed
+            case UNDEFINED:
+            default:
+                return TransactionStatus.UNKNOWN;
+        }
+    }
+
+    public static OperationResult toOperationResult(@Nullable final PaymentTransactionInfoPlugin paymentTransactionInfoPlugin) {
+        if (paymentTransactionInfoPlugin == null || paymentTransactionInfoPlugin.getStatus() == null) {
+            return OperationResult.EXCEPTION;
+        }
+
+        switch (paymentTransactionInfoPlugin.getStatus()) {
+            case PROCESSED:
+                return OperationResult.SUCCESS;
+            case PENDING:
+                return OperationResult.PENDING;
+            case ERROR:
+                return OperationResult.FAILURE;
+            // Might change later if Janitor fixes the state
+            case UNDEFINED:
+            default:
+                return OperationResult.EXCEPTION;
+        }
+    }
+}
diff --git a/payment/src/main/java/org/killbill/billing/payment/core/PluginRoutingPaymentProcessor.java b/payment/src/main/java/org/killbill/billing/payment/core/PluginRoutingPaymentProcessor.java
index a6ca074..a4cd186 100644
--- a/payment/src/main/java/org/killbill/billing/payment/core/PluginRoutingPaymentProcessor.java
+++ b/payment/src/main/java/org/killbill/billing/payment/core/PluginRoutingPaymentProcessor.java
@@ -40,7 +40,7 @@ import org.killbill.billing.payment.api.PaymentApiException;
 import org.killbill.billing.payment.api.PluginProperty;
 import org.killbill.billing.payment.api.TransactionType;
 import org.killbill.billing.payment.core.sm.PluginRoutingPaymentAutomatonRunner;
-import org.killbill.billing.payment.core.sm.RetryStateMachineHelper;
+import org.killbill.billing.payment.core.sm.PaymentControlStateMachineHelper;
 import org.killbill.billing.payment.dao.PaymentAttemptModelDao;
 import org.killbill.billing.payment.dao.PaymentDao;
 import org.killbill.billing.payment.dao.PaymentModelDao;
@@ -64,7 +64,7 @@ public class PluginRoutingPaymentProcessor extends ProcessorBase {
     private static final Joiner JOINER = Joiner.on(", ");
 
     private final PluginRoutingPaymentAutomatonRunner pluginControlledPaymentAutomatonRunner;
-    private final RetryStateMachineHelper retrySMHelper;
+    private final PaymentControlStateMachineHelper paymentControlStateMachineHelper;
 
     @Inject
     public PluginRoutingPaymentProcessor(final OSGIServiceRegistration<PaymentPluginApi> pluginRegistry,
@@ -76,10 +76,10 @@ public class PluginRoutingPaymentProcessor extends ProcessorBase {
                                          @Named(PLUGIN_EXECUTOR_NAMED) final ExecutorService executor,
                                          final InternalCallContextFactory internalCallContextFactory,
                                          final PluginRoutingPaymentAutomatonRunner pluginControlledPaymentAutomatonRunner,
-                                         final RetryStateMachineHelper retrySMHelper,
+                                         final PaymentControlStateMachineHelper paymentControlStateMachineHelper,
                                          final Clock clock) {
         super(pluginRegistry, accountInternalApi, paymentDao, tagUserApi, locker, executor, internalCallContextFactory, invoiceApi, clock);
-        this.retrySMHelper = retrySMHelper;
+        this.paymentControlStateMachineHelper = paymentControlStateMachineHelper;
         this.pluginControlledPaymentAutomatonRunner = pluginControlledPaymentAutomatonRunner;
     }
 
@@ -135,7 +135,7 @@ public class PluginRoutingPaymentProcessor extends ProcessorBase {
     }
 
     public Payment createVoid(final boolean isApiPayment, final Account account, final UUID paymentId, final String transactionExternalKey,
-                              final Iterable<PluginProperty> properties, final CallContext callContext, final InternalCallContext internalCallContext) throws PaymentApiException {
+                              final Iterable<PluginProperty> properties, final List<String> paymentControlPluginNames, final CallContext callContext, final InternalCallContext internalCallContext) throws PaymentApiException {
         return pluginControlledPaymentAutomatonRunner.run(isApiPayment,
                                                           TransactionType.VOID,
                                                           account,
@@ -146,7 +146,7 @@ public class PluginRoutingPaymentProcessor extends ProcessorBase {
                                                           null,
                                                           null,
                                                           properties,
-                                                          null,
+                                                          paymentControlPluginNames,
                                                           callContext, internalCallContext);
     }
 
@@ -183,9 +183,9 @@ public class PluginRoutingPaymentProcessor extends ProcessorBase {
                                                           callContext, internalCallContext);
     }
 
-    public Payment createChargeback(final Account account, final UUID paymentId, final String transactionExternalKey, final BigDecimal amount, final Currency currency,
+    public Payment createChargeback(final boolean isApiPayment, final Account account, final UUID paymentId, final String transactionExternalKey, final BigDecimal amount, final Currency currency,
                                     final List<String> paymentControlPluginNames, final CallContext callContext, final InternalCallContext internalCallContext) throws PaymentApiException {
-        return pluginControlledPaymentAutomatonRunner.run(true,
+        return pluginControlledPaymentAutomatonRunner.run(isApiPayment,
                                                           TransactionType.CHARGEBACK,
                                                           account,
                                                           null,
@@ -209,7 +209,7 @@ public class PluginRoutingPaymentProcessor extends ProcessorBase {
             final Account account = accountInternalApi.getAccountById(attempt.getAccountId(), internalCallContext);
             final CallContext callContext = buildCallContext(internalCallContext);
 
-            final State state = retrySMHelper.getState(attempt.getStateName());
+            final State state = paymentControlStateMachineHelper.getState(attempt.getStateName());
             pluginControlledPaymentAutomatonRunner.run(state,
                                                        false,
                                                        attempt.getTransactionType(),
diff --git a/payment/src/main/java/org/killbill/billing/payment/core/sm/PaymentAutomatonDAOHelper.java b/payment/src/main/java/org/killbill/billing/payment/core/sm/PaymentAutomatonDAOHelper.java
index 4f72dae..8db48a9 100644
--- a/payment/src/main/java/org/killbill/billing/payment/core/sm/PaymentAutomatonDAOHelper.java
+++ b/payment/src/main/java/org/killbill/billing/payment/core/sm/PaymentAutomatonDAOHelper.java
@@ -101,7 +101,7 @@ public class PaymentAutomatonDAOHelper {
         paymentStateContext.setOnLeavingStateExistingTransactions(existingTransactions);
     }
 
-    public void processPaymentInfoPlugin(final TransactionStatus paymentStatus, @Nullable final PaymentTransactionInfoPlugin paymentInfoPlugin,
+    public void processPaymentInfoPlugin(final TransactionStatus transactionStatus, @Nullable final PaymentTransactionInfoPlugin paymentInfoPlugin,
                                          final String currentPaymentStateName) {
         final BigDecimal processedAmount = paymentInfoPlugin == null ? null : paymentInfoPlugin.getAmount();
         final Currency processedCurrency = paymentInfoPlugin == null ? null : paymentInfoPlugin.getCurrency();
@@ -115,7 +115,7 @@ public class PaymentAutomatonDAOHelper {
                                                            currentPaymentStateName,
                                                            lastSuccessPaymentState,
                                                            paymentStateContext.getPaymentTransactionModelDao().getId(),
-                                                           paymentStatus,
+                                                           transactionStatus,
                                                            processedAmount,
                                                            processedCurrency,
                                                            gatewayErrorCode,
@@ -149,6 +149,10 @@ public class PaymentAutomatonDAOHelper {
         return eventBus;
     }
 
+    public PaymentDao getPaymentDao() {
+        return paymentDao;
+    }
+
     private PaymentModelDao buildNewPaymentModelDao() {
         final DateTime createdDate = utcNow;
         final DateTime updatedDate = utcNow;
diff --git a/payment/src/main/java/org/killbill/billing/payment/core/sm/PaymentAutomatonRunner.java b/payment/src/main/java/org/killbill/billing/payment/core/sm/PaymentAutomatonRunner.java
index aed47f5..099ea97 100644
--- a/payment/src/main/java/org/killbill/billing/payment/core/sm/PaymentAutomatonRunner.java
+++ b/payment/src/main/java/org/killbill/billing/payment/core/sm/PaymentAutomatonRunner.java
@@ -45,6 +45,27 @@ import org.killbill.billing.osgi.api.OSGIServiceRegistration;
 import org.killbill.billing.payment.api.PaymentApiException;
 import org.killbill.billing.payment.api.PluginProperty;
 import org.killbill.billing.payment.api.TransactionType;
+import org.killbill.billing.payment.core.sm.payments.AuthorizeCompleted;
+import org.killbill.billing.payment.core.sm.payments.AuthorizeInitiated;
+import org.killbill.billing.payment.core.sm.payments.AuthorizeOperation;
+import org.killbill.billing.payment.core.sm.payments.CaptureCompleted;
+import org.killbill.billing.payment.core.sm.payments.CaptureInitiated;
+import org.killbill.billing.payment.core.sm.payments.CaptureOperation;
+import org.killbill.billing.payment.core.sm.payments.ChargebackCompleted;
+import org.killbill.billing.payment.core.sm.payments.ChargebackInitiated;
+import org.killbill.billing.payment.core.sm.payments.ChargebackOperation;
+import org.killbill.billing.payment.core.sm.payments.CreditCompleted;
+import org.killbill.billing.payment.core.sm.payments.CreditInitiated;
+import org.killbill.billing.payment.core.sm.payments.CreditOperation;
+import org.killbill.billing.payment.core.sm.payments.PurchaseCompleted;
+import org.killbill.billing.payment.core.sm.payments.PurchaseInitiated;
+import org.killbill.billing.payment.core.sm.payments.PurchaseOperation;
+import org.killbill.billing.payment.core.sm.payments.RefundCompleted;
+import org.killbill.billing.payment.core.sm.payments.RefundInitiated;
+import org.killbill.billing.payment.core.sm.payments.RefundOperation;
+import org.killbill.billing.payment.core.sm.payments.VoidCompleted;
+import org.killbill.billing.payment.core.sm.payments.VoidInitiated;
+import org.killbill.billing.payment.core.sm.payments.VoidOperation;
 import org.killbill.billing.payment.dao.PaymentDao;
 import org.killbill.billing.payment.dao.PaymentModelDao;
 import org.killbill.billing.payment.dispatcher.PluginDispatcher;
@@ -176,6 +197,17 @@ public class PaymentAutomatonRunner {
         return paymentStateContext.getPaymentId();
     }
 
+    //
+    // TODO Fix fields accessed by some callbacks (which are not injected)
+    //
+    public PaymentDao getPaymentDao() {
+        return paymentDao;
+    }
+
+    public Clock getClock() {
+        return clock;
+    }
+
     protected void runStateMachineOperation(final String initialStateName, final TransactionType transactionType,
                                             final LeavingStateCallback leavingStateCallback, final OperationCallback operationCallback, final EnteringStateCallback enteringStateCallback,
                                             final UUID accountId, final String invoiceId) throws PaymentApiException {
diff --git a/payment/src/main/java/org/killbill/billing/payment/core/sm/PaymentStateContext.java b/payment/src/main/java/org/killbill/billing/payment/core/sm/PaymentStateContext.java
index 28af33a..034fbc4 100644
--- a/payment/src/main/java/org/killbill/billing/payment/core/sm/PaymentStateContext.java
+++ b/payment/src/main/java/org/killbill/billing/payment/core/sm/PaymentStateContext.java
@@ -1,8 +1,8 @@
 /*
- * Copyright 2014 Groupon, Inc
- * Copyright 2014 The Billing Project, LLC
+ * Copyright 2014-2015 Groupon, Inc
+ * Copyright 2014-2015 The Billing Project, LLC
  *
- * Groupon licenses this file to you under the Apache License, version 2.0
+ * The Billing Project licenses this file to you under the Apache License, version 2.0
  * (the "License"); you may not use this file except in compliance with the
  * License.  You may obtain a copy of the License at:
  *
@@ -44,11 +44,12 @@ public class PaymentStateContext {
     // Stateful objects created by the callbacks and passed to the other following callbacks in the automaton
     protected List<PaymentTransactionModelDao> onLeavingStateExistingTransactions;
     protected PaymentTransactionModelDao paymentTransactionModelDao;
-    protected PaymentTransactionInfoPlugin paymentInfoPlugin;
+    protected PaymentTransactionInfoPlugin paymentTransactionInfoPlugin;
     protected BigDecimal amount;
     protected String paymentExternalKey;
     protected String paymentTransactionExternalKey;
     protected Currency currency;
+    protected Iterable<PluginProperty> properties;
 
     // Can be updated later via paymentTransactionModelDao (e.g. for auth or purchase)
     protected final UUID paymentId;
@@ -56,7 +57,6 @@ public class PaymentStateContext {
     protected final Account account;
     protected final TransactionType transactionType;
     protected final boolean shouldLockAccountAndDispatch;
-    protected final Iterable<PluginProperty> properties;
     protected final InternalCallContext internalCallContext;
     protected final CallContext callContext;
     protected final boolean isApiPayment;
@@ -120,12 +120,12 @@ public class PaymentStateContext {
         this.onLeavingStateExistingTransactions = onLeavingStateExistingTransactions;
     }
 
-    public PaymentTransactionInfoPlugin getPaymentInfoPlugin() {
-        return paymentInfoPlugin;
+    public PaymentTransactionInfoPlugin getPaymentTransactionInfoPlugin() {
+        return paymentTransactionInfoPlugin;
     }
 
-    public void setPaymentInfoPlugin(final PaymentTransactionInfoPlugin paymentInfoPlugin) {
-        this.paymentInfoPlugin = paymentInfoPlugin;
+    public void setPaymentTransactionInfoPlugin(final PaymentTransactionInfoPlugin paymentTransactionInfoPlugin) {
+        this.paymentTransactionInfoPlugin = paymentTransactionInfoPlugin;
     }
 
     public UUID getPaymentId() {
diff --git a/payment/src/main/java/org/killbill/billing/payment/core/sm/PluginRoutingPaymentAutomatonRunner.java b/payment/src/main/java/org/killbill/billing/payment/core/sm/PluginRoutingPaymentAutomatonRunner.java
index 1731335..7836409 100644
--- a/payment/src/main/java/org/killbill/billing/payment/core/sm/PluginRoutingPaymentAutomatonRunner.java
+++ b/payment/src/main/java/org/killbill/billing/payment/core/sm/PluginRoutingPaymentAutomatonRunner.java
@@ -42,6 +42,18 @@ import org.killbill.billing.payment.api.PaymentApiException;
 import org.killbill.billing.payment.api.PluginProperty;
 import org.killbill.billing.payment.api.TransactionType;
 import org.killbill.billing.payment.core.PaymentProcessor;
+import org.killbill.billing.payment.core.sm.control.AuthorizeControlOperation;
+import org.killbill.billing.payment.core.sm.control.CaptureControlOperation;
+import org.killbill.billing.payment.core.sm.control.ChargebackControlOperation;
+import org.killbill.billing.payment.core.sm.control.CompletionControlOperation;
+import org.killbill.billing.payment.core.sm.control.CreditControlOperation;
+import org.killbill.billing.payment.core.sm.control.DefaultControlCompleted;
+import org.killbill.billing.payment.core.sm.control.DefaultControlInitiated;
+import org.killbill.billing.payment.core.sm.control.NoopControlInitiated;
+import org.killbill.billing.payment.core.sm.control.PurchaseControlOperation;
+import org.killbill.billing.payment.core.sm.control.RefundControlOperation;
+import org.killbill.billing.payment.core.sm.control.VoidControlOperation;
+import org.killbill.billing.payment.core.sm.control.PaymentStateControlContext;
 import org.killbill.billing.payment.dao.PaymentDao;
 import org.killbill.billing.payment.glue.PaymentModule;
 import org.killbill.billing.payment.plugin.api.PaymentPluginApi;
@@ -64,19 +76,19 @@ public class PluginRoutingPaymentAutomatonRunner extends PaymentAutomatonRunner 
     private final PaymentProcessor paymentProcessor;
     private final RetryServiceScheduler retryServiceScheduler;
 
-    private final RetryStateMachineHelper retrySMHelper;
+    private final PaymentControlStateMachineHelper paymentControlStateMachineHelper;
 
     protected final OSGIServiceRegistration<PaymentRoutingPluginApi> paymentControlPluginRegistry;
 
     @Inject
     public PluginRoutingPaymentAutomatonRunner(@Named(PaymentModule.STATE_MACHINE_PAYMENT) final StateMachineConfig stateMachineConfig, final PaymentDao paymentDao, final GlobalLocker locker, final OSGIServiceRegistration<PaymentPluginApi> pluginRegistry,
-                                               final OSGIServiceRegistration<PaymentRoutingPluginApi> retryPluginRegistry, final Clock clock, final PaymentProcessor paymentProcessor, @Named(RETRYABLE_NAMED) final RetryServiceScheduler retryServiceScheduler,
-                                               final PaymentConfig paymentConfig, @com.google.inject.name.Named(PLUGIN_EXECUTOR_NAMED) final ExecutorService executor, final PaymentStateMachineHelper paymentSMHelper, final RetryStateMachineHelper retrySMHelper, final PersistentBus eventBus) {
+                                               final OSGIServiceRegistration<PaymentRoutingPluginApi> paymentControlPluginRegistry, final Clock clock, final PaymentProcessor paymentProcessor, @Named(RETRYABLE_NAMED) final RetryServiceScheduler retryServiceScheduler,
+                                               final PaymentConfig paymentConfig, @com.google.inject.name.Named(PLUGIN_EXECUTOR_NAMED) final ExecutorService executor, final PaymentStateMachineHelper paymentSMHelper, final PaymentControlStateMachineHelper paymentControlStateMachineHelper, final PersistentBus eventBus) {
         super(stateMachineConfig, paymentConfig, paymentDao, locker, pluginRegistry, clock, executor, eventBus, paymentSMHelper);
         this.paymentProcessor = paymentProcessor;
-        this.paymentControlPluginRegistry = retryPluginRegistry;
+        this.paymentControlPluginRegistry = paymentControlPluginRegistry;
         this.retryServiceScheduler = retryServiceScheduler;
-        this.retrySMHelper = retrySMHelper;
+        this.paymentControlStateMachineHelper = paymentControlStateMachineHelper;
     }
 
     public Payment run(final boolean isApiPayment, final TransactionType transactionType, final Account account, @Nullable final UUID paymentMethodId,
@@ -84,7 +96,7 @@ public class PluginRoutingPaymentAutomatonRunner extends PaymentAutomatonRunner 
                        @Nullable final BigDecimal amount, @Nullable final Currency currency,
                        final Iterable<PluginProperty> properties, @Nullable final List<String> paymentControlPluginNames,
                        final CallContext callContext, final InternalCallContext internalCallContext) throws PaymentApiException {
-        return run(retrySMHelper.getInitialState(), isApiPayment, transactionType, account, paymentMethodId, paymentId, paymentExternalKey, paymentTransactionExternalKey,
+        return run(paymentControlStateMachineHelper.getInitialState(), isApiPayment, transactionType, account, paymentMethodId, paymentId, paymentExternalKey, paymentTransactionExternalKey,
                    amount, currency, properties, paymentControlPluginNames, callContext, internalCallContext);
     }
 
@@ -93,17 +105,17 @@ public class PluginRoutingPaymentAutomatonRunner extends PaymentAutomatonRunner 
                        @Nullable final BigDecimal amount, @Nullable final Currency currency,
                        final Iterable<PluginProperty> properties, @Nullable final List<String> paymentControlPluginNames,
                        final CallContext callContext, final InternalCallContext internalCallContext) throws PaymentApiException {
-        final RetryablePaymentStateContext paymentStateContext = createContext(isApiPayment, transactionType, account, paymentMethodId,
+        final PaymentStateControlContext paymentStateContext = createContext(isApiPayment, transactionType, account, paymentMethodId,
                                                                                paymentId, paymentExternalKey,
                                                                                paymentTransactionExternalKey,
                                                                                amount, currency,
                                                                                properties, paymentControlPluginNames, callContext, internalCallContext);
         try {
             final OperationCallback callback = createOperationCallback(transactionType, paymentStateContext);
-            final LeavingStateCallback leavingStateCallback = new RetryLeavingStateCallback(this, paymentStateContext, paymentDao, retrySMHelper.getInitialState(), retrySMHelper.getRetriedState(), transactionType);
-            final EnteringStateCallback enteringStateCallback = new RetryEnteringStateCallback(this, paymentStateContext, retryServiceScheduler);
+            final LeavingStateCallback leavingStateCallback = new DefaultControlInitiated(this, paymentStateContext, paymentDao, paymentControlStateMachineHelper.getInitialState(), paymentControlStateMachineHelper.getRetriedState(), transactionType);
+            final EnteringStateCallback enteringStateCallback = new DefaultControlCompleted(this, paymentStateContext, retryServiceScheduler);
 
-            state.runOperation(retrySMHelper.getRetryOperation(), callback, enteringStateCallback, leavingStateCallback);
+            state.runOperation(paymentControlStateMachineHelper.getOperation(), callback, enteringStateCallback, leavingStateCallback);
         } catch (final MissingEntryException e) {
             throw new PaymentApiException(e.getCause(), ErrorCode.PAYMENT_INTERNAL_ERROR, Objects.firstNonNull(e.getMessage(), ""));
         } catch (final OperationException e) {
@@ -119,13 +131,13 @@ public class PluginRoutingPaymentAutomatonRunner extends PaymentAutomatonRunner 
         return paymentStateContext.getResult();
     }
 
-    public Payment completeRun(final RetryablePaymentStateContext paymentStateContext) throws PaymentApiException {
+    public Payment completeRun(final PaymentStateControlContext paymentStateContext) throws PaymentApiException {
         try {
-            final OperationCallback callback = new RetryCompletionOperationCallback(locker, paymentPluginDispatcher, paymentStateContext, paymentProcessor, paymentControlPluginRegistry);
-            final LeavingStateCallback leavingStateCallback = new RetryNoopLeavingStateCallback();
-            final EnteringStateCallback enteringStateCallback = new RetryEnteringStateCallback(this, paymentStateContext, retryServiceScheduler);
+            final OperationCallback callback = new CompletionControlOperation(locker, paymentPluginDispatcher, paymentStateContext, paymentProcessor, paymentControlPluginRegistry);
+            final LeavingStateCallback leavingStateCallback = new NoopControlInitiated();
+            final EnteringStateCallback enteringStateCallback = new DefaultControlCompleted(this, paymentStateContext, retryServiceScheduler);
 
-            retrySMHelper.getInitialState().runOperation(retrySMHelper.getRetryOperation(), callback, enteringStateCallback, leavingStateCallback);
+            paymentControlStateMachineHelper.getInitialState().runOperation(paymentControlStateMachineHelper.getOperation(), callback, enteringStateCallback, leavingStateCallback);
         } catch (final MissingEntryException e) {
             throw new PaymentApiException(e.getCause(), ErrorCode.PAYMENT_INTERNAL_ERROR, Objects.firstNonNull(e.getMessage(), ""));
         } catch (final OperationException e) {
@@ -141,42 +153,43 @@ public class PluginRoutingPaymentAutomatonRunner extends PaymentAutomatonRunner 
     }
 
     @VisibleForTesting
-    RetryablePaymentStateContext createContext(final boolean isApiPayment, final TransactionType transactionType, final Account account, @Nullable final UUID paymentMethodId,
+    PaymentStateControlContext createContext(final boolean isApiPayment, final TransactionType transactionType, final Account account, @Nullable final UUID paymentMethodId,
                                                @Nullable final UUID paymentId, @Nullable final String paymentExternalKey, final String paymentTransactionExternalKey,
                                                @Nullable final BigDecimal amount, @Nullable final Currency currency, final Iterable<PluginProperty> properties,
                                                final List<String> paymentControlPluginNames, final CallContext callContext, final InternalCallContext internalCallContext) throws PaymentApiException {
-        return new RetryablePaymentStateContext(paymentControlPluginNames, isApiPayment, paymentId, paymentExternalKey, paymentTransactionExternalKey, transactionType, account,
+        return new PaymentStateControlContext(paymentControlPluginNames, isApiPayment, paymentId, paymentExternalKey, paymentTransactionExternalKey, transactionType, account,
                                                 paymentMethodId, amount, currency, properties, internalCallContext, callContext);
     }
 
     @VisibleForTesting
-    OperationCallback createOperationCallback(final TransactionType transactionType, final RetryablePaymentStateContext paymentStateContext) {
+    OperationCallback createOperationCallback(final TransactionType transactionType, final PaymentStateControlContext paymentStateContext) {
         final OperationCallback callback;
         switch (transactionType) {
             case AUTHORIZE:
-                callback = new RetryAuthorizeOperationCallback(locker, paymentPluginDispatcher, paymentStateContext, paymentProcessor, paymentControlPluginRegistry);
+                callback = new AuthorizeControlOperation(locker, paymentPluginDispatcher, paymentStateContext, paymentProcessor, paymentControlPluginRegistry);
                 break;
             case CAPTURE:
-                callback = new RetryCaptureOperationCallback(locker, paymentPluginDispatcher, paymentStateContext, paymentProcessor, paymentControlPluginRegistry);
+                callback = new CaptureControlOperation(locker, paymentPluginDispatcher, paymentStateContext, paymentProcessor, paymentControlPluginRegistry);
                 break;
             case PURCHASE:
-                callback = new RetryPurchaseOperationCallback(locker, paymentPluginDispatcher, paymentStateContext, paymentProcessor, paymentControlPluginRegistry);
+                callback = new PurchaseControlOperation(locker, paymentPluginDispatcher, paymentStateContext, paymentProcessor, paymentControlPluginRegistry);
                 break;
             case VOID:
-                callback = new RetryVoidOperationCallback(locker, paymentPluginDispatcher, paymentStateContext, paymentProcessor, paymentControlPluginRegistry);
+                callback = new VoidControlOperation(locker, paymentPluginDispatcher, paymentStateContext, paymentProcessor, paymentControlPluginRegistry);
                 break;
             case CREDIT:
-                callback = new RetryCreditOperationCallback(locker, paymentPluginDispatcher, paymentStateContext, paymentProcessor, paymentControlPluginRegistry);
+                callback = new CreditControlOperation(locker, paymentPluginDispatcher, paymentStateContext, paymentProcessor, paymentControlPluginRegistry);
                 break;
             case REFUND:
-                callback = new RetryRefundOperationCallback(locker, paymentPluginDispatcher, paymentStateContext, paymentProcessor, paymentControlPluginRegistry);
+                callback = new RefundControlOperation(locker, paymentPluginDispatcher, paymentStateContext, paymentProcessor, paymentControlPluginRegistry);
                 break;
             case CHARGEBACK:
-                callback = new RetryChargebackOperationCallback(locker, paymentPluginDispatcher, paymentStateContext, paymentProcessor, paymentControlPluginRegistry);
+                callback = new ChargebackControlOperation(locker, paymentPluginDispatcher, paymentStateContext, paymentProcessor, paymentControlPluginRegistry);
                 break;
             default:
                 throw new IllegalStateException("Unsupported transaction type " + transactionType);
         }
         return callback;
     }
+
 }
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 d8f2486..c1f1203 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
@@ -20,8 +20,11 @@ package org.killbill.billing.payment.dao;
 
 import java.math.BigDecimal;
 import java.util.Collection;
+import java.util.HashMap;
 import java.util.Iterator;
+import java.util.LinkedList;
 import java.util.List;
+import java.util.Map;
 import java.util.UUID;
 
 import javax.annotation.Nullable;
@@ -60,6 +63,7 @@ import com.google.common.base.Preconditions;
 import com.google.common.base.Predicate;
 import com.google.common.collect.Collections2;
 import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
 
 public class DefaultPaymentDao implements PaymentDao {
 
@@ -117,12 +121,12 @@ public class DefaultPaymentDao implements PaymentDao {
     }
 
     @Override
-    public List<PaymentAttemptModelDao> getPaymentAttemptsByState(final String stateName, final DateTime createdBeforeDate, final InternalTenantContext context) {
+    public List<PaymentAttemptModelDao> getPaymentAttemptsByStateAcrossTenants(final String stateName, final DateTime createdBeforeDate) {
         return transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<List<PaymentAttemptModelDao>>() {
             @Override
             public List<PaymentAttemptModelDao> inTransaction(final EntitySqlDaoWrapperFactory entitySqlDaoWrapperFactory) throws Exception {
                 final PaymentAttemptSqlDao transactional = entitySqlDaoWrapperFactory.become(PaymentAttemptSqlDao.class);
-                return transactional.getByStateName(stateName, createdBeforeDate.toDate(), context);
+                return transactional.getByStateNameAcrossTenants(stateName, createdBeforeDate.toDate());
             }
         });
     }
@@ -152,28 +156,23 @@ public class DefaultPaymentDao implements PaymentDao {
     }
 
     @Override
-    public int failOldPendingTransactions(final TransactionStatus newTransactionStatus, final DateTime createdBeforeDate, final InternalCallContext context) {
-        return transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<Integer>() {
+    public List<PaymentTransactionModelDao> getByTransactionStatusAcrossTenants(final Iterable<TransactionStatus> transactionStatuses, final DateTime createdBeforeDate, final DateTime createdAfterDate, final int limit) {
+        return transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<List<PaymentTransactionModelDao>>() {
             @Override
-            public Integer inTransaction(final EntitySqlDaoWrapperFactory entitySqlDaoWrapperFactory) throws Exception {
+            public List<PaymentTransactionModelDao> inTransaction(final EntitySqlDaoWrapperFactory entitySqlDaoWrapperFactory) throws Exception {
                 final TransactionSqlDao transactional = entitySqlDaoWrapperFactory.become(TransactionSqlDao.class);
-                final List<PaymentTransactionModelDao> oldPendingTransactions = transactional.getByTransactionStatusPriorDate(TransactionStatus.PENDING.toString(), createdBeforeDate.toDate(), context);
-                if (oldPendingTransactions.size() > 0) {
-                    final Collection<String> oldPendingTransactionIds = Collections2.transform(oldPendingTransactions, new Function<PaymentTransactionModelDao, String>() {
-                        @Override
-                        public String apply(final PaymentTransactionModelDao input) {
-                            return input.getId().toString();
-                        }
-                    });
-
-
-                    return transactional.failOldPendingTransactions(oldPendingTransactionIds, TransactionStatus.PAYMENT_FAILURE.toString(), context);
-                }
-                return 0;
+                final Collection<String> allTransactionStatus = ImmutableList.copyOf(Iterables.transform(transactionStatuses, new Function<TransactionStatus, String>() {
+                    @Override
+                    public String apply(final TransactionStatus input) {
+                        return input.toString();
+                    }
+                }));
+                 return transactional.getByTransactionStatusPriorDateAcrossTenants(allTransactionStatus, createdBeforeDate.toDate(), createdAfterDate.toDate(), limit);
             }
         });
     }
 
+
     @Override
     public List<PaymentTransactionModelDao> getPaymentTransactionsByExternalKey(final String transactionExternalKey, final InternalTenantContext context) {
         return transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<List<PaymentTransactionModelDao>>() {
@@ -324,11 +323,11 @@ public class DefaultPaymentDao implements PaymentDao {
     }
 
     @Override
-    public List<PaymentModelDao> getPaymentsByStates(final String[] states, final DateTime createdBeforeDate, final DateTime createdAfterDate, final int limit, final InternalTenantContext context) {
+    public List<PaymentModelDao> getPaymentsByStatesAcrossTenants(final String[] states, final DateTime createdBeforeDate, final DateTime createdAfterDate, final int limit) {
         return transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<List<PaymentModelDao>>() {
             @Override
             public List<PaymentModelDao> inTransaction(final EntitySqlDaoWrapperFactory entitySqlDaoWrapperFactory) throws Exception {
-                return entitySqlDaoWrapperFactory.become(PaymentSqlDao.class).getPaymentsByStates(ImmutableList.copyOf(states), createdBeforeDate.toDate(), createdAfterDate.toDate(), context, limit);
+                return entitySqlDaoWrapperFactory.become(PaymentSqlDao.class).getPaymentsByStatesAcrossTenants(ImmutableList.copyOf(states), createdBeforeDate.toDate(), createdAfterDate.toDate(), limit);
             }
         });
     }
diff --git a/payment/src/main/java/org/killbill/billing/payment/dao/PaymentAttemptModelDao.java b/payment/src/main/java/org/killbill/billing/payment/dao/PaymentAttemptModelDao.java
index dfbb709..823036d 100644
--- a/payment/src/main/java/org/killbill/billing/payment/dao/PaymentAttemptModelDao.java
+++ b/payment/src/main/java/org/killbill/billing/payment/dao/PaymentAttemptModelDao.java
@@ -25,6 +25,7 @@ import javax.annotation.Nullable;
 import org.joda.time.DateTime;
 import org.killbill.billing.catalog.api.Currency;
 import org.killbill.billing.payment.api.TransactionType;
+import org.killbill.billing.util.UUIDs;
 import org.killbill.billing.util.dao.TableName;
 import org.killbill.billing.util.entity.Entity;
 import org.killbill.billing.util.entity.dao.EntityModelDao;
@@ -71,7 +72,7 @@ public class PaymentAttemptModelDao extends EntityModelDaoBase implements Entity
     public PaymentAttemptModelDao(final UUID accountId, final UUID paymentMethodId, @Nullable final DateTime createdDate, @Nullable final DateTime updatedDate,
                                   final String paymentExternalKey, final UUID transactionId, final String transactionExternalKey, final TransactionType transactionType, final String stateName,
                                   final BigDecimal amount, final Currency currency, final List<String> paymentControlPluginNames, final byte[] pluginProperties) {
-        this(accountId, paymentMethodId, UUID.randomUUID(), createdDate, updatedDate, paymentExternalKey, transactionId, transactionExternalKey, transactionType, stateName,
+        this(accountId, paymentMethodId, UUIDs.randomUUID(), createdDate, updatedDate, paymentExternalKey, transactionId, transactionExternalKey, transactionType, stateName,
              amount, currency, toPluginNames(paymentControlPluginNames), pluginProperties);
     }
 
diff --git a/payment/src/main/java/org/killbill/billing/payment/dao/PaymentAttemptSqlDao.java b/payment/src/main/java/org/killbill/billing/payment/dao/PaymentAttemptSqlDao.java
index e3a3fe4..544e02e 100644
--- a/payment/src/main/java/org/killbill/billing/payment/dao/PaymentAttemptSqlDao.java
+++ b/payment/src/main/java/org/killbill/billing/payment/dao/PaymentAttemptSqlDao.java
@@ -50,8 +50,7 @@ public interface PaymentAttemptSqlDao extends EntitySqlDao<PaymentAttemptModelDa
                                                          @BindBean final InternalTenantContext context);
 
     @SqlQuery
-    List<PaymentAttemptModelDao> getByStateName(@Bind("stateName") final String stateName,
-                                                @Bind("createdBeforeDate") final Date createdBeforeDate,
-                                                @BindBean final InternalTenantContext context);
+    List<PaymentAttemptModelDao> getByStateNameAcrossTenants(@Bind("stateName") final String stateName,
+                                                             @Bind("createdBeforeDate") final Date createdBeforeDate);
 
 }
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 1119b85..2ac7ea1 100644
--- a/payment/src/main/java/org/killbill/billing/payment/dao/PaymentDao.java
+++ b/payment/src/main/java/org/killbill/billing/payment/dao/PaymentDao.java
@@ -30,13 +30,13 @@ import org.killbill.billing.util.entity.Pagination;
 
 public interface PaymentDao {
 
-    public int failOldPendingTransactions(TransactionStatus newTransactionStatus, DateTime createdBeforeDate, InternalCallContext context);
+    public List<PaymentTransactionModelDao> getByTransactionStatusAcrossTenants(final Iterable<TransactionStatus> transactionStatuses, DateTime createdBeforeDate, DateTime createdAfterDate, int limit);
 
     public PaymentAttemptModelDao insertPaymentAttemptWithProperties(PaymentAttemptModelDao attempt, InternalCallContext context);
 
     public void updatePaymentAttempt(UUID paymentAttemptId, UUID transactionId, String state, InternalCallContext context);
 
-    public List<PaymentAttemptModelDao> getPaymentAttemptsByState(String stateName, DateTime createdBeforeDate, InternalTenantContext context);
+    public List<PaymentAttemptModelDao> getPaymentAttemptsByStateAcrossTenants(String stateName, DateTime createdBeforeDate);
 
     public List<PaymentAttemptModelDao> getPaymentAttempts(String paymentExternalKey, InternalTenantContext context);
 
@@ -64,7 +64,7 @@ public interface PaymentDao {
 
     public List<PaymentModelDao> getPaymentsForAccount(UUID accountId, InternalTenantContext context);
 
-    public List<PaymentModelDao> getPaymentsByStates(String [] states, DateTime createdBeforeDate, DateTime createdAfterDate, int limit, InternalTenantContext context);
+    public List<PaymentModelDao> getPaymentsByStatesAcrossTenants(String[] states, DateTime createdBeforeDate, DateTime createdAfterDate, int limit);
 
     public List<PaymentTransactionModelDao> getTransactionsForAccount(UUID accountId, InternalTenantContext context);
 
diff --git a/payment/src/main/java/org/killbill/billing/payment/dao/PaymentModelDao.java b/payment/src/main/java/org/killbill/billing/payment/dao/PaymentModelDao.java
index a670f23..612fed7 100644
--- a/payment/src/main/java/org/killbill/billing/payment/dao/PaymentModelDao.java
+++ b/payment/src/main/java/org/killbill/billing/payment/dao/PaymentModelDao.java
@@ -25,6 +25,7 @@ import org.killbill.billing.payment.api.Payment;
 import org.killbill.billing.util.dao.TableName;
 import org.killbill.billing.util.entity.dao.EntityModelDao;
 import org.killbill.billing.util.entity.dao.EntityModelDaoBase;
+import org.killbill.billing.util.UUIDs;
 
 import com.google.common.base.Objects;
 
@@ -52,7 +53,7 @@ public class PaymentModelDao extends EntityModelDaoBase implements EntityModelDa
 
     public PaymentModelDao(@Nullable final DateTime createdDate, @Nullable final DateTime updatedDate, final UUID accountId,
                            final UUID paymentMethodId, @Nullable final String externalKey) {
-        this(UUID.randomUUID(), createdDate, updatedDate, accountId, paymentMethodId, INVALID_PAYMENT_NUMBER, externalKey);
+        this(UUIDs.randomUUID(), createdDate, updatedDate, accountId, paymentMethodId, INVALID_PAYMENT_NUMBER, externalKey);
     }
 
     public UUID getAccountId() { return accountId; }
diff --git a/payment/src/main/java/org/killbill/billing/payment/dao/PaymentSqlDao.java b/payment/src/main/java/org/killbill/billing/payment/dao/PaymentSqlDao.java
index 4d8dd0c..9e9ed12 100644
--- a/payment/src/main/java/org/killbill/billing/payment/dao/PaymentSqlDao.java
+++ b/payment/src/main/java/org/killbill/billing/payment/dao/PaymentSqlDao.java
@@ -62,11 +62,10 @@ public interface PaymentSqlDao extends EntitySqlDao<PaymentModelDao, Payment> {
                                                    @BindBean final InternalTenantContext context);
 
     @SqlQuery
-    public List<PaymentModelDao> getPaymentsByStates(@StateCollectionBinder final Collection<String> states,
-                                                     @Bind("createdBeforeDate") final Date createdBeforeDate,
-                                                     @Bind("createdAfterDate") final Date createdAfterDate,
-                                                     @BindBean final InternalTenantContext context,
-                                                     @Bind("limit") final int limit);
+    public List<PaymentModelDao> getPaymentsByStatesAcrossTenants(@StateCollectionBinder final Collection<String> states,
+                                                                  @Bind("createdBeforeDate") final Date createdBeforeDate,
+                                                                  @Bind("createdAfterDate") final Date createdAfterDate,
+                                                                  @Bind("limit") final int limit);
 
     @SqlQuery
     @SmartFetchSize(shouldStream = true)
diff --git a/payment/src/main/java/org/killbill/billing/payment/dao/PaymentTransactionModelDao.java b/payment/src/main/java/org/killbill/billing/payment/dao/PaymentTransactionModelDao.java
index 5b6d422..ac0f0da 100644
--- a/payment/src/main/java/org/killbill/billing/payment/dao/PaymentTransactionModelDao.java
+++ b/payment/src/main/java/org/killbill/billing/payment/dao/PaymentTransactionModelDao.java
@@ -26,6 +26,7 @@ import org.killbill.billing.catalog.api.Currency;
 import org.killbill.billing.payment.api.PaymentTransaction;
 import org.killbill.billing.payment.api.TransactionStatus;
 import org.killbill.billing.payment.api.TransactionType;
+import org.killbill.billing.util.UUIDs;
 import org.killbill.billing.util.dao.TableName;
 import org.killbill.billing.util.entity.dao.EntityModelDao;
 import org.killbill.billing.util.entity.dao.EntityModelDaoBase;
@@ -71,7 +72,7 @@ public class PaymentTransactionModelDao extends EntityModelDaoBase implements En
     public PaymentTransactionModelDao(@Nullable final DateTime createdDate, @Nullable final DateTime updatedDate, @Nullable final UUID attemptId,
                                       @Nullable final String transactionExternalKey, final UUID paymentId, final TransactionType transactionType, final DateTime effectiveDate,
                                       final TransactionStatus paymentStatus, final BigDecimal amount, final Currency currency, final String gatewayErrorCode, final String gatewayErrorMsg) {
-        this(UUID.randomUUID(), attemptId, transactionExternalKey, createdDate, updatedDate, paymentId, transactionType, effectiveDate, paymentStatus, amount, currency, gatewayErrorCode, gatewayErrorMsg);
+        this(UUIDs.randomUUID(), attemptId, transactionExternalKey, createdDate, updatedDate, paymentId, transactionType, effectiveDate, paymentStatus, amount, currency, gatewayErrorCode, gatewayErrorMsg);
     }
 
     public UUID getPaymentId() {
diff --git a/payment/src/main/java/org/killbill/billing/payment/dao/TransactionSqlDao.java b/payment/src/main/java/org/killbill/billing/payment/dao/TransactionSqlDao.java
index e9abc96..d291ca0 100644
--- a/payment/src/main/java/org/killbill/billing/payment/dao/TransactionSqlDao.java
+++ b/payment/src/main/java/org/killbill/billing/payment/dao/TransactionSqlDao.java
@@ -29,7 +29,6 @@ import org.killbill.billing.util.audit.ChangeType;
 import org.killbill.billing.util.entity.dao.Audited;
 import org.killbill.billing.util.entity.dao.EntitySqlDao;
 import org.killbill.billing.util.entity.dao.EntitySqlDaoStringTemplate;
-import org.killbill.billing.util.tag.dao.UUIDCollectionBinder;
 import org.skife.jdbi.v2.sqlobject.Bind;
 import org.skife.jdbi.v2.sqlobject.BindBean;
 import org.skife.jdbi.v2.sqlobject.SqlQuery;
@@ -53,15 +52,10 @@ public interface TransactionSqlDao extends EntitySqlDao<PaymentTransactionModelD
                                                                          @BindBean final InternalTenantContext context);
 
     @SqlQuery
-    List<PaymentTransactionModelDao> getByTransactionStatusPriorDate(@Bind("transactionStatus") final String transactionStatus,
-                                                                     @Bind("beforeCreatedDate") final Date beforeCreatedDate,
-                                                                     @BindBean final InternalTenantContext context);
-
-    @SqlUpdate
-    @Audited(ChangeType.UPDATE)
-    int failOldPendingTransactions(@UUIDCollectionBinder final Collection<String> pendingTransactionIds,
-                                    @Bind("newTransactionStatus") final String newTransactionStatus,
-                                    @BindBean final InternalCallContext context);
+    List<PaymentTransactionModelDao> getByTransactionStatusPriorDateAcrossTenants(@TransactionStatusCollectionBinder final Collection<String> statuses,
+                                                                                  @Bind("createdBeforeDate") final Date createdBeforeDate,
+                                                                                  @Bind("createdAfterDate") final Date createdAfterDate,
+                                                                                  @Bind("limit") final int limit);
 
     @SqlQuery
     public List<PaymentTransactionModelDao> getByPaymentId(@Bind("paymentId") final UUID paymentId,
diff --git a/payment/src/main/java/org/killbill/billing/payment/dao/TransactionStatusCollectionBinder.java b/payment/src/main/java/org/killbill/billing/payment/dao/TransactionStatusCollectionBinder.java
new file mode 100644
index 0000000..cd8973d
--- /dev/null
+++ b/payment/src/main/java/org/killbill/billing/payment/dao/TransactionStatusCollectionBinder.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2014-2015 Groupon, Inc
+ * Copyright 2014-2015 The Billing Project, LLC
+ *
+ * The Billing Project licenses this file to you under the Apache License, version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at:
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.killbill.billing.payment.dao;
+
+import java.lang.annotation.Annotation;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import java.util.Collection;
+
+import org.killbill.billing.payment.dao.TransactionStatusCollectionBinder.TransactionStatusCollectionBinderFactory;
+import org.skife.jdbi.v2.SQLStatement;
+import org.skife.jdbi.v2.sqlobject.Binder;
+import org.skife.jdbi.v2.sqlobject.BinderFactory;
+import org.skife.jdbi.v2.sqlobject.BindingAnnotation;
+
+@BindingAnnotation(TransactionStatusCollectionBinderFactory.class)
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.PARAMETER})
+public @interface TransactionStatusCollectionBinder {
+
+    public static class TransactionStatusCollectionBinderFactory implements BinderFactory {
+
+        @Override
+        public Binder build(Annotation annotation) {
+            return new Binder<TransactionStatusCollectionBinder, Collection<String>>() {
+
+                @Override
+                public void bind(SQLStatement<?> query, TransactionStatusCollectionBinder bind, Collection<String> allTransactionStatus) {
+                    query.define("statuses", allTransactionStatus);
+
+                    int idx = 0;
+                    for (String transactionStatus : allTransactionStatus) {
+                        query.bind("status_" + idx, transactionStatus);
+                        idx++;
+                    }
+                }
+            };
+        }
+    }
+}
diff --git a/payment/src/main/java/org/killbill/billing/payment/glue/PaymentModule.java b/payment/src/main/java/org/killbill/billing/payment/glue/PaymentModule.java
index 7bcf32e..4bd9153 100644
--- a/payment/src/main/java/org/killbill/billing/payment/glue/PaymentModule.java
+++ b/payment/src/main/java/org/killbill/billing/payment/glue/PaymentModule.java
@@ -29,6 +29,8 @@ import javax.inject.Provider;
 import org.killbill.automaton.DefaultStateMachineConfig;
 import org.killbill.automaton.StateMachineConfig;
 import org.killbill.billing.osgi.api.OSGIServiceRegistration;
+import org.killbill.billing.payment.api.AdminPaymentApi;
+import org.killbill.billing.payment.api.DefaultAdminPaymentApi;
 import org.killbill.billing.payment.api.DefaultPaymentApi;
 import org.killbill.billing.payment.api.DefaultPaymentGatewayApi;
 import org.killbill.billing.payment.api.PaymentApi;
@@ -44,7 +46,7 @@ import org.killbill.billing.payment.core.PaymentProcessor;
 import org.killbill.billing.payment.core.PluginRoutingPaymentProcessor;
 import org.killbill.billing.payment.core.sm.PaymentStateMachineHelper;
 import org.killbill.billing.payment.core.sm.PluginRoutingPaymentAutomatonRunner;
-import org.killbill.billing.payment.core.sm.RetryStateMachineHelper;
+import org.killbill.billing.payment.core.sm.PaymentControlStateMachineHelper;
 import org.killbill.billing.payment.dao.DefaultPaymentDao;
 import org.killbill.billing.payment.dao.PaymentDao;
 import org.killbill.billing.payment.plugin.api.PaymentPluginApi;
@@ -114,7 +116,7 @@ public class PaymentModule extends KillBillModule {
 
         bind(StateMachineProvider.class).annotatedWith(Names.named(STATE_MACHINE_RETRY)).toInstance(new StateMachineProvider(DEFAULT_STATE_MACHINE_RETRY_XML));
         bind(StateMachineConfig.class).annotatedWith(Names.named(STATE_MACHINE_RETRY)).toProvider(Key.get(StateMachineProvider.class, Names.named(STATE_MACHINE_RETRY)));
-        bind(RetryStateMachineHelper.class).asEagerSingleton();
+        bind(PaymentControlStateMachineHelper.class).asEagerSingleton();
 
         bind(StateMachineProvider.class).annotatedWith(Names.named(STATE_MACHINE_PAYMENT)).toInstance(new StateMachineProvider(DEFAULT_STATE_MACHINE_PAYMENT_XML));
         bind(StateMachineConfig.class).annotatedWith(Names.named(STATE_MACHINE_PAYMENT)).toProvider(Key.get(StateMachineProvider.class, Names.named(STATE_MACHINE_PAYMENT)));
@@ -175,6 +177,7 @@ public class PaymentModule extends KillBillModule {
 
         bind(PaymentApi.class).to(DefaultPaymentApi.class).asEagerSingleton();
         bind(PaymentGatewayApi.class).to(DefaultPaymentGatewayApi.class).asEagerSingleton();
+        bind(AdminPaymentApi.class).to(DefaultAdminPaymentApi.class).asEagerSingleton();
         bind(InvoiceHandler.class).asEagerSingleton();
         bind(PaymentTagHandler.class).asEagerSingleton();
         bind(PaymentService.class).to(DefaultPaymentService.class).asEagerSingleton();
diff --git a/payment/src/main/java/org/killbill/billing/payment/invoice/InvoicePaymentRoutingPluginApi.java b/payment/src/main/java/org/killbill/billing/payment/invoice/InvoicePaymentRoutingPluginApi.java
index e6a4ad4..e1b5ddf 100644
--- a/payment/src/main/java/org/killbill/billing/payment/invoice/InvoicePaymentRoutingPluginApi.java
+++ b/payment/src/main/java/org/killbill/billing/payment/invoice/InvoicePaymentRoutingPluginApi.java
@@ -126,7 +126,7 @@ public final class InvoicePaymentRoutingPluginApi implements PaymentRoutingPlugi
             case REFUND:
                 return getPluginRefundResult(paymentRoutingContext, internalContext);
             case CHARGEBACK:
-                return new DefaultPriorPaymentRoutingResult(false, paymentRoutingContext.getAmount(), null, null);
+                return new DefaultPriorPaymentRoutingResult(false, paymentRoutingContext.getAmount());
             default:
                 throw new IllegalStateException("Unexpected transactionType " + transactionType);
         }
@@ -244,7 +244,7 @@ public final class InvoicePaymentRoutingPluginApi implements PaymentRoutingPlugi
                                                      " aborted : invoice balance is = " + invoice.getBalance() +
                                                      ", requested payment amount is = " + paymentRoutingPluginContext.getAmount());
             } else {
-                return new DefaultPriorPaymentRoutingResult(isAborted, requestedAmount, null, null);
+                return new DefaultPriorPaymentRoutingResult(isAborted, requestedAmount);
             }
         } catch (final InvoiceApiException e) {
             throw new PaymentRoutingApiException(e);
@@ -277,7 +277,7 @@ public final class InvoicePaymentRoutingPluginApi implements PaymentRoutingPlugi
                                                  " aborted : invoice item sum amount is " + amountToBeRefunded +
                                                  ", requested refund amount is = " + paymentRoutingPluginContext.getAmount());
         } else {
-            return new DefaultPriorPaymentRoutingResult(isAborted, amountToBeRefunded, null, null);
+            return new DefaultPriorPaymentRoutingResult(isAborted, amountToBeRefunded);
         }
     }
 
@@ -450,8 +450,8 @@ public final class InvoicePaymentRoutingPluginApi implements PaymentRoutingPlugi
             inputAmount != null &&
             invoice.getBalance().compareTo(inputAmount) < 0) {
             log.info("Invoice " + invoice.getId() +
-                        " has a balance of " + invoice.getBalance().floatValue() +
-                        " less than retry payment amount of " + inputAmount.floatValue());
+                     " has a balance of " + invoice.getBalance().floatValue() +
+                     " less than retry payment amount of " + inputAmount.floatValue());
             return BigDecimal.ZERO;
         }
         if (inputAmount == null) {
diff --git a/payment/src/main/java/org/killbill/billing/payment/provider/DefaultNoOpPaymentMethodPlugin.java b/payment/src/main/java/org/killbill/billing/payment/provider/DefaultNoOpPaymentMethodPlugin.java
index ca5ce3a..6c94e2f 100644
--- a/payment/src/main/java/org/killbill/billing/payment/provider/DefaultNoOpPaymentMethodPlugin.java
+++ b/payment/src/main/java/org/killbill/billing/payment/provider/DefaultNoOpPaymentMethodPlugin.java
@@ -25,6 +25,7 @@ import javax.annotation.Nullable;
 
 import org.killbill.billing.payment.api.PaymentMethodPlugin;
 import org.killbill.billing.payment.api.PluginProperty;
+import org.killbill.billing.util.UUIDs;
 
 import com.google.common.collect.ImmutableList;
 
@@ -37,7 +38,7 @@ public class DefaultNoOpPaymentMethodPlugin implements PaymentMethodPlugin {
 
     public DefaultNoOpPaymentMethodPlugin(final UUID kbPaymentMethodId, final PaymentMethodPlugin src) {
         this.kbPaymentMethodId = kbPaymentMethodId;
-        this.externalId = UUID.randomUUID().toString();
+        this.externalId = UUIDs.randomUUID().toString();
         this.isDefault = src.isDefaultPaymentMethod();
         this.props = src.getProperties();
     }
diff --git a/payment/src/main/java/org/killbill/billing/payment/provider/DefaultNoOpPaymentRoutingProviderPlugin.java b/payment/src/main/java/org/killbill/billing/payment/provider/DefaultNoOpPaymentRoutingProviderPlugin.java
index 9b89e1d..3b11dec 100644
--- a/payment/src/main/java/org/killbill/billing/payment/provider/DefaultNoOpPaymentRoutingProviderPlugin.java
+++ b/payment/src/main/java/org/killbill/billing/payment/provider/DefaultNoOpPaymentRoutingProviderPlugin.java
@@ -1,7 +1,8 @@
 /*
- * Copyright 2014 Groupon, Inc
+ * Copyright 2014-2015 Groupon, Inc
+ * Copyright 2014-2015 The Billing Project, LLC
  *
- * Groupon licenses this file to you under the Apache License, version 2.0
+ * The Billing Project licenses this file to you under the Apache License, version 2.0
  * (the "License"); you may not use this file except in compliance with the
  * License.  You may obtain a copy of the License at:
  *
@@ -34,17 +35,17 @@ public class DefaultNoOpPaymentRoutingProviderPlugin implements PaymentRoutingPl
     private DateTime nextRetryDate;
 
     @Override
-    public PriorPaymentRoutingResult priorCall(final PaymentRoutingContext retryPluginContext, Iterable<PluginProperty> properties) throws PaymentRoutingApiException {
-        return new DefaultPriorPaymentRoutingResult(isRetryAborted, null, null, null);
+    public PriorPaymentRoutingResult priorCall(final PaymentRoutingContext retryPluginContext, final Iterable<PluginProperty> properties) throws PaymentRoutingApiException {
+        return new DefaultPriorPaymentRoutingResult(isRetryAborted);
     }
 
     @Override
-    public OnSuccessPaymentRoutingResult onSuccessCall(final PaymentRoutingContext paymentControlContext, Iterable<PluginProperty> properties) throws PaymentRoutingApiException {
+    public OnSuccessPaymentRoutingResult onSuccessCall(final PaymentRoutingContext paymentControlContext, final Iterable<PluginProperty> properties) throws PaymentRoutingApiException {
         return null;
     }
 
     @Override
-    public OnFailurePaymentRoutingResult onFailureCall(final PaymentRoutingContext paymentControlContext, Iterable<PluginProperty> properties) throws PaymentRoutingApiException {
+    public OnFailurePaymentRoutingResult onFailureCall(final PaymentRoutingContext paymentControlContext, final Iterable<PluginProperty> properties) throws PaymentRoutingApiException {
         return new DefaultFailureCallResult(nextRetryDate);
     }
 
diff --git a/payment/src/main/java/org/killbill/billing/payment/provider/DefaultPaymentRoutingProviderPlugin.java b/payment/src/main/java/org/killbill/billing/payment/provider/DefaultPaymentRoutingProviderPlugin.java
index 6830fd9..5f6e510 100644
--- a/payment/src/main/java/org/killbill/billing/payment/provider/DefaultPaymentRoutingProviderPlugin.java
+++ b/payment/src/main/java/org/killbill/billing/payment/provider/DefaultPaymentRoutingProviderPlugin.java
@@ -1,7 +1,8 @@
 /*
- * Copyright 2014 Groupon, Inc
+ * Copyright 2014-2015 Groupon, Inc
+ * Copyright 2014-2015 The Billing Project, LLC
  *
- * Groupon licenses this file to you under the Apache License, version 2.0
+ * The Billing Project licenses this file to you under the Apache License, version 2.0
  * (the "License"); you may not use this file except in compliance with the
  * License.  You may obtain a copy of the License at:
  *
@@ -31,17 +32,17 @@ public class DefaultPaymentRoutingProviderPlugin implements PaymentRoutingPlugin
     public static final String PLUGIN_NAME = "__DEFAULT_PAYMENT_CONTROL__";
 
     @Override
-    public PriorPaymentRoutingResult priorCall(final PaymentRoutingContext paymentControlContext, Iterable<PluginProperty> properties) throws PaymentRoutingApiException {
-        return new DefaultPriorPaymentRoutingResult(false, null, null, null);
+    public PriorPaymentRoutingResult priorCall(final PaymentRoutingContext paymentControlContext, final Iterable<PluginProperty> properties) throws PaymentRoutingApiException {
+        return new DefaultPriorPaymentRoutingResult(false);
     }
 
     @Override
-    public OnSuccessPaymentRoutingResult onSuccessCall(final PaymentRoutingContext paymentControlContext, Iterable<PluginProperty> properties) {
+    public OnSuccessPaymentRoutingResult onSuccessCall(final PaymentRoutingContext paymentControlContext, final Iterable<PluginProperty> properties) {
         return null;
     }
 
     @Override
-    public OnFailurePaymentRoutingResult onFailureCall(final PaymentRoutingContext paymentControlContext, Iterable<PluginProperty> properties) {
+    public OnFailurePaymentRoutingResult onFailureCall(final PaymentRoutingContext paymentControlContext, final Iterable<PluginProperty> properties) {
         return new DefaultFailureCallResult(null);
     }
 }
diff --git a/payment/src/main/java/org/killbill/billing/payment/retry/DefaultPriorPaymentRoutingResult.java b/payment/src/main/java/org/killbill/billing/payment/retry/DefaultPriorPaymentRoutingResult.java
index f0b8f61..7e48e2c 100644
--- a/payment/src/main/java/org/killbill/billing/payment/retry/DefaultPriorPaymentRoutingResult.java
+++ b/payment/src/main/java/org/killbill/billing/payment/retry/DefaultPriorPaymentRoutingResult.java
@@ -1,7 +1,8 @@
 /*
- * Copyright 2014 Groupon, Inc
+ * Copyright 2014-2015 Groupon, Inc
+ * Copyright 2014-2015 The Billing Project, LLC
  *
- * Groupon licenses this file to you under the Apache License, version 2.0
+ * The Billing Project licenses this file to you under the Apache License, version 2.0
  * (the "License"); you may not use this file except in compliance with the
  * License.  You may obtain a copy of the License at:
  *
@@ -20,6 +21,7 @@ import java.math.BigDecimal;
 import java.util.UUID;
 
 import org.killbill.billing.catalog.api.Currency;
+import org.killbill.billing.payment.api.PluginProperty;
 import org.killbill.billing.routing.plugin.api.PriorPaymentRoutingResult;
 
 public class DefaultPriorPaymentRoutingResult implements PriorPaymentRoutingResult {
@@ -28,19 +30,26 @@ public class DefaultPriorPaymentRoutingResult implements PriorPaymentRoutingResu
     private final BigDecimal adjustedRetryAmount;
     private final Currency adjustedCurrency;
     private final UUID adjustedPaymentMethodId;
+    private final Iterable<PluginProperty> adjustedPluginProperties;
 
     public DefaultPriorPaymentRoutingResult(final boolean isAborted,
                                             final BigDecimal adjustedRetryAmount,
                                             final Currency adjustedCurrency,
-                                            final UUID adjustedPaymentMethodId) {
+                                            final UUID adjustedPaymentMethodId,
+                                            final Iterable<PluginProperty> adjustedPluginProperties) {
         this.isAborted = isAborted;
         this.adjustedRetryAmount = adjustedRetryAmount;
         this.adjustedCurrency = adjustedCurrency;
         this.adjustedPaymentMethodId = adjustedPaymentMethodId;
+        this.adjustedPluginProperties = adjustedPluginProperties;
+    }
+
+    public DefaultPriorPaymentRoutingResult(final boolean isAborted, final BigDecimal adjustedRetryAmount) {
+        this(isAborted, adjustedRetryAmount, null, null, null);
     }
 
     public DefaultPriorPaymentRoutingResult(final boolean isAborted) {
-        this(isAborted, null, null, null);
+        this(isAborted, null);
     }
 
     @Override
@@ -62,4 +71,9 @@ public class DefaultPriorPaymentRoutingResult implements PriorPaymentRoutingResu
     public UUID getAdjustedPaymentMethodId() {
         return adjustedPaymentMethodId;
     }
+
+    @Override
+    public Iterable<PluginProperty> getAdjustedPluginProperties() {
+        return adjustedPluginProperties;
+    }
 }
diff --git a/payment/src/main/resources/org/killbill/billing/payment/dao/PaymentAttemptSqlDao.sql.stg b/payment/src/main/resources/org/killbill/billing/payment/dao/PaymentAttemptSqlDao.sql.stg
index 1cc0604..c1f77a9 100644
--- a/payment/src/main/resources/org/killbill/billing/payment/dao/PaymentAttemptSqlDao.sql.stg
+++ b/payment/src/main/resources/org/killbill/billing/payment/dao/PaymentAttemptSqlDao.sql.stg
@@ -65,7 +65,7 @@ where payment_external_key = :paymentExternalKey
 >>
 
 /* Does not include tenant info, global */
-getByStateName() ::= <<
+getByStateNameAcrossTenants() ::= <<
 select
 <allTableFields("")>
 from <tableName()>
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 3a96d8f..0d8d592 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
@@ -102,13 +102,13 @@ where pm.plugin_name = :pluginName
 ;
 >>
 
-getPaymentsByStates(states) ::= <<
+getPaymentsByStatesAcrossTenants(states) ::= <<
 select
 <allTableFields("t.")>
 from <tableName()> t
 where
 created_date >= :createdAfterDate
-and created_date \<= :createdBeforeDate
+and created_date \< :createdBeforeDate
 and state_name in (<states: {state | :state_<i0>}; separator="," >)
 limit :limit
 ;
diff --git a/payment/src/main/resources/org/killbill/billing/payment/dao/TransactionSqlDao.sql.stg b/payment/src/main/resources/org/killbill/billing/payment/dao/TransactionSqlDao.sql.stg
index 932a405..1d54780 100644
--- a/payment/src/main/resources/org/killbill/billing/payment/dao/TransactionSqlDao.sql.stg
+++ b/payment/src/main/resources/org/killbill/billing/payment/dao/TransactionSqlDao.sql.stg
@@ -82,21 +82,15 @@ where payment_id = :paymentId
 
 
 /* Does not include AND_CHECK_TENANT() since this is a global operation */
-getByTransactionStatusPriorDate() ::= <<
+getByTransactionStatusPriorDateAcrossTenants(statuses) ::= <<
 select <allTableFields()>
 from <tableName()>
-where transaction_status = :transactionStatus
-and created_date \< :beforeCreatedDate
+where
+created_date >= :createdAfterDate
+and created_date \< :createdBeforeDate
+and transaction_status in (<statuses: {status | :status_<i0>}; separator="," >)
 <defaultOrderBy()>
+limit :limit
 ;
 >>
 
-
-failOldPendingTransactions(ids) ::= <<
-update <tableName()>
-set transaction_status = :newTransactionStatus
-, updated_by = :updatedBy
-, updated_date = :createdDate
-where <idField("")> in (<ids: {id | :id_<i0>}; separator="," >)
-;
->>
diff --git a/payment/src/test/java/org/killbill/billing/payment/api/TestPaymentApi.java b/payment/src/test/java/org/killbill/billing/payment/api/TestPaymentApi.java
index 16d02f8..e5bb1ee 100644
--- a/payment/src/test/java/org/killbill/billing/payment/api/TestPaymentApi.java
+++ b/payment/src/test/java/org/killbill/billing/payment/api/TestPaymentApi.java
@@ -536,7 +536,7 @@ public class TestPaymentApi extends PaymentTestSuiteWithEmbeddedDB {
         } catch (final PaymentApiException e) {
             Assert.assertEquals(e.getCode(), ErrorCode.PAYMENT_NO_SUCH_PAYMENT.getCode());
 
-            final Payment latestPayment = paymentApi.getPayment(initialPayment.getId(), false, ImmutableList.<PluginProperty>of(), callContext);
+            final Payment latestPayment = paymentApi.getPayment(initialPayment.getId(), true, ImmutableList.<PluginProperty>of(), callContext);
             assertEquals(latestPayment, initialPayment);
         }
     }
@@ -556,7 +556,7 @@ public class TestPaymentApi extends PaymentTestSuiteWithEmbeddedDB {
         } catch (final PaymentApiException e) {
             Assert.assertEquals(e.getCode(), ErrorCode.PAYMENT_INVALID_PARAMETER.getCode());
 
-            final Payment latestPayment = paymentApi.getPayment(initialPayment.getId(), false, ImmutableList.<PluginProperty>of(), callContext);
+            final Payment latestPayment = paymentApi.getPayment(initialPayment.getId(), true, ImmutableList.<PluginProperty>of(), callContext);
             assertEquals(latestPayment, initialPayment);
         }
     }
diff --git a/payment/src/test/java/org/killbill/billing/payment/core/sm/MockRetryablePaymentAutomatonRunner.java b/payment/src/test/java/org/killbill/billing/payment/core/sm/MockRetryablePaymentAutomatonRunner.java
index 749123c..62e0d44 100644
--- a/payment/src/test/java/org/killbill/billing/payment/core/sm/MockRetryablePaymentAutomatonRunner.java
+++ b/payment/src/test/java/org/killbill/billing/payment/core/sm/MockRetryablePaymentAutomatonRunner.java
@@ -36,6 +36,7 @@ import org.killbill.billing.payment.api.PaymentApiException;
 import org.killbill.billing.payment.api.PluginProperty;
 import org.killbill.billing.payment.api.TransactionType;
 import org.killbill.billing.payment.core.PaymentProcessor;
+import org.killbill.billing.payment.core.sm.control.PaymentStateControlContext;
 import org.killbill.billing.payment.dao.PaymentDao;
 import org.killbill.billing.payment.dispatcher.PluginDispatcher;
 import org.killbill.billing.payment.glue.PaymentModule;
@@ -55,17 +56,17 @@ import static org.killbill.billing.payment.glue.PaymentModule.RETRYABLE_NAMED;
 public class MockRetryablePaymentAutomatonRunner extends PluginRoutingPaymentAutomatonRunner {
 
     private OperationCallback operationCallback;
-    private RetryablePaymentStateContext context;
+    private PaymentStateControlContext context;
 
     @Inject
     public MockRetryablePaymentAutomatonRunner(@Named(PaymentModule.STATE_MACHINE_PAYMENT) final StateMachineConfig stateMachineConfig, @Named(PaymentModule.STATE_MACHINE_RETRY) final StateMachineConfig retryStateMachine, final PaymentDao paymentDao, final GlobalLocker locker, final OSGIServiceRegistration<PaymentPluginApi> pluginRegistry, final OSGIServiceRegistration<PaymentRoutingPluginApi> retryPluginRegistry, final Clock clock, final TagInternalApi tagApi, final PaymentProcessor paymentProcessor,
                                                @Named(RETRYABLE_NAMED) final RetryServiceScheduler retryServiceScheduler, final PaymentConfig paymentConfig, @com.google.inject.name.Named(PLUGIN_EXECUTOR_NAMED) final ExecutorService executor,
-                                               final PaymentStateMachineHelper paymentSMHelper, final RetryStateMachineHelper retrySMHelper, final PersistentBus eventBus) {
+                                               final PaymentStateMachineHelper paymentSMHelper, final PaymentControlStateMachineHelper retrySMHelper, final PersistentBus eventBus) {
         super(stateMachineConfig, paymentDao, locker, pluginRegistry, retryPluginRegistry, clock, paymentProcessor, retryServiceScheduler, paymentConfig, executor, paymentSMHelper, retrySMHelper, eventBus);
     }
 
     @Override
-    OperationCallback createOperationCallback(final TransactionType transactionType, final RetryablePaymentStateContext paymentStateContext) {
+    OperationCallback createOperationCallback(final TransactionType transactionType, final PaymentStateControlContext paymentStateContext) {
         if (operationCallback == null) {
             return super.createOperationCallback(transactionType, paymentStateContext);
         } else {
@@ -74,7 +75,7 @@ public class MockRetryablePaymentAutomatonRunner extends PluginRoutingPaymentAut
     }
 
     @Override
-    RetryablePaymentStateContext createContext(final boolean isApiPayment, final TransactionType transactionType, final Account account, @Nullable final UUID paymentMethodId,
+    PaymentStateControlContext createContext(final boolean isApiPayment, final TransactionType transactionType, final Account account, @Nullable final UUID paymentMethodId,
                                                      @Nullable final UUID paymentId, @Nullable final String paymentExternalKey, final String paymentTransactionExternalKey,
                                                      @Nullable final BigDecimal amount, @Nullable final Currency currency,
                                                      final Iterable<PluginProperty> properties,
@@ -92,7 +93,7 @@ public class MockRetryablePaymentAutomatonRunner extends PluginRoutingPaymentAut
         return this;
     }
 
-    public MockRetryablePaymentAutomatonRunner setContext(final RetryablePaymentStateContext context) {
+    public MockRetryablePaymentAutomatonRunner setContext(final PaymentStateControlContext context) {
         this.context = context;
         return this;
     }
diff --git a/payment/src/test/java/org/killbill/billing/payment/core/sm/MockRetryAuthorizeOperationCallback.java b/payment/src/test/java/org/killbill/billing/payment/core/sm/MockRetryAuthorizeOperationCallback.java
index 949f245..8ae7806 100644
--- a/payment/src/test/java/org/killbill/billing/payment/core/sm/MockRetryAuthorizeOperationCallback.java
+++ b/payment/src/test/java/org/killbill/billing/payment/core/sm/MockRetryAuthorizeOperationCallback.java
@@ -27,6 +27,8 @@ import org.killbill.billing.payment.api.PaymentTransaction;
 import org.killbill.billing.payment.api.PaymentApiException;
 import org.killbill.billing.payment.api.TransactionStatus;
 import org.killbill.billing.payment.core.PaymentProcessor;
+import org.killbill.billing.payment.core.sm.control.AuthorizeControlOperation;
+import org.killbill.billing.payment.core.sm.control.PaymentStateControlContext;
 import org.killbill.billing.payment.dao.PaymentDao;
 import org.killbill.billing.payment.dao.PaymentModelDao;
 import org.killbill.billing.payment.dao.PaymentTransactionModelDao;
@@ -35,7 +37,7 @@ import org.killbill.billing.routing.plugin.api.PaymentRoutingPluginApi;
 import org.killbill.clock.Clock;
 import org.killbill.commons.locker.GlobalLocker;
 
-public class MockRetryAuthorizeOperationCallback extends RetryAuthorizeOperationCallback {
+public class MockRetryAuthorizeOperationCallback extends AuthorizeControlOperation {
 
     private final PaymentDao paymentDao;
     private final Clock clock;
@@ -43,7 +45,7 @@ public class MockRetryAuthorizeOperationCallback extends RetryAuthorizeOperation
     private Exception exception;
     private OperationResult result;
 
-    public MockRetryAuthorizeOperationCallback(final GlobalLocker locker, final PluginDispatcher<OperationResult> paymentPluginDispatcher, final RetryablePaymentStateContext paymentStateContext, final PaymentProcessor paymentProcessor, final OSGIServiceRegistration<PaymentRoutingPluginApi> retryPluginRegistry, final PaymentDao paymentDao, final Clock clock) {
+    public MockRetryAuthorizeOperationCallback(final GlobalLocker locker, final PluginDispatcher<OperationResult> paymentPluginDispatcher, final PaymentStateControlContext paymentStateContext, final PaymentProcessor paymentProcessor, final OSGIServiceRegistration<PaymentRoutingPluginApi> retryPluginRegistry, final PaymentDao paymentDao, final Clock clock) {
         super(locker, paymentPluginDispatcher, paymentStateContext, paymentProcessor, retryPluginRegistry);
         this.paymentDao = paymentDao;
         this.clock = clock;
diff --git a/payment/src/test/java/org/killbill/billing/payment/core/sm/TestPaymentEnteringStateCallback.java b/payment/src/test/java/org/killbill/billing/payment/core/sm/TestPaymentEnteringStateCallback.java
index 7cc35b5..c4f53e6 100644
--- a/payment/src/test/java/org/killbill/billing/payment/core/sm/TestPaymentEnteringStateCallback.java
+++ b/payment/src/test/java/org/killbill/billing/payment/core/sm/TestPaymentEnteringStateCallback.java
@@ -31,6 +31,7 @@ import org.killbill.billing.payment.api.PaymentApiException;
 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.payments.PaymentEnteringStateCallback;
 import org.killbill.billing.payment.dao.PaymentTransactionModelDao;
 import org.killbill.billing.payment.plugin.api.PaymentPluginStatus;
 import org.killbill.billing.payment.plugin.api.PaymentTransactionInfoPlugin;
@@ -88,7 +89,7 @@ public class TestPaymentEnteringStateCallback extends PaymentTestSuiteWithEmbedd
         Mockito.when(paymentInfoPlugin.getStatus()).thenReturn(PaymentPluginStatus.PENDING);
         Mockito.when(paymentInfoPlugin.getGatewayErrorCode()).thenReturn(UUID.randomUUID().toString().substring(0, 5));
         Mockito.when(paymentInfoPlugin.getGatewayError()).thenReturn(UUID.randomUUID().toString());
-        paymentStateContext.setPaymentInfoPlugin(paymentInfoPlugin);
+        paymentStateContext.setPaymentTransactionInfoPlugin(paymentInfoPlugin);
 
         // Process the plugin result
         callback.enteringState(state, operationCallback, operationResult, leavingStateCallback);
@@ -108,7 +109,7 @@ public class TestPaymentEnteringStateCallback extends PaymentTestSuiteWithEmbedd
     public void testEnterStateWithOperationException() throws Exception {
         daoHelper.createNewPaymentTransaction();
         // Simulate a bug in the plugin - i.e. nothing was returned
-        paymentStateContext.setPaymentInfoPlugin(null);
+        paymentStateContext.setPaymentTransactionInfoPlugin(null);
         operationResult = OperationResult.EXCEPTION;
 
         callback.enteringState(state, operationCallback, operationResult, leavingStateCallback);
diff --git a/payment/src/test/java/org/killbill/billing/payment/core/sm/TestPaymentLeavingStateCallback.java b/payment/src/test/java/org/killbill/billing/payment/core/sm/TestPaymentLeavingStateCallback.java
index c37f577..0097eaf 100644
--- a/payment/src/test/java/org/killbill/billing/payment/core/sm/TestPaymentLeavingStateCallback.java
+++ b/payment/src/test/java/org/killbill/billing/payment/core/sm/TestPaymentLeavingStateCallback.java
@@ -30,6 +30,7 @@ import org.killbill.billing.payment.api.PaymentApiException;
 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.payments.PaymentLeavingStateCallback;
 import org.killbill.billing.payment.dao.PaymentModelDao;
 import org.killbill.billing.payment.dao.PaymentTransactionModelDao;
 import org.mockito.Mockito;
diff --git a/payment/src/test/java/org/killbill/billing/payment/core/sm/TestPaymentOperation.java b/payment/src/test/java/org/killbill/billing/payment/core/sm/TestPaymentOperation.java
index 4d11b09..9f6eb58 100644
--- a/payment/src/test/java/org/killbill/billing/payment/core/sm/TestPaymentOperation.java
+++ b/payment/src/test/java/org/killbill/billing/payment/core/sm/TestPaymentOperation.java
@@ -31,6 +31,7 @@ import org.killbill.billing.payment.PaymentTestSuiteNoDB;
 import org.killbill.billing.payment.api.PaymentApiException;
 import org.killbill.billing.payment.api.PluginProperty;
 import org.killbill.billing.payment.api.TransactionType;
+import org.killbill.billing.payment.core.sm.payments.PaymentOperation;
 import org.killbill.billing.payment.dao.PaymentDao;
 import org.killbill.billing.payment.dao.PaymentMethodModelDao;
 import org.killbill.billing.payment.dispatcher.PluginDispatcher;
@@ -55,18 +56,18 @@ public class TestPaymentOperation extends PaymentTestSuiteNoDB {
     public void testPaymentFailure() throws Exception {
         setUp(PaymentPluginStatus.ERROR);
 
-        Assert.assertNull(paymentStateContext.getPaymentInfoPlugin());
+        Assert.assertNull(paymentStateContext.getPaymentTransactionInfoPlugin());
 
         Assert.assertEquals(paymentOperation.doOperationCallback(), OperationResult.FAILURE);
 
-        Assert.assertNotNull(paymentStateContext.getPaymentInfoPlugin());
+        Assert.assertNotNull(paymentStateContext.getPaymentTransactionInfoPlugin());
     }
 
     @Test(groups = "fast")
     public void testPluginFailure() throws Exception {
         setUp(null);
 
-        Assert.assertNull(paymentStateContext.getPaymentInfoPlugin());
+        Assert.assertNull(paymentStateContext.getPaymentTransactionInfoPlugin());
 
         try {
             paymentOperation.doOperationCallback();
@@ -75,29 +76,29 @@ public class TestPaymentOperation extends PaymentTestSuiteNoDB {
             Assert.assertEquals(e.getOperationResult(), OperationResult.EXCEPTION);
         }
 
-        Assert.assertNull(paymentStateContext.getPaymentInfoPlugin());
+        Assert.assertNull(paymentStateContext.getPaymentTransactionInfoPlugin());
     }
 
     @Test(groups = "fast")
     public void testPaymentPending() throws Exception {
         setUp(PaymentPluginStatus.PENDING);
 
-        Assert.assertNull(paymentStateContext.getPaymentInfoPlugin());
+        Assert.assertNull(paymentStateContext.getPaymentTransactionInfoPlugin());
 
         Assert.assertEquals(paymentOperation.doOperationCallback(), OperationResult.PENDING);
 
-        Assert.assertNotNull(paymentStateContext.getPaymentInfoPlugin());
+        Assert.assertNotNull(paymentStateContext.getPaymentTransactionInfoPlugin());
     }
 
     @Test(groups = "fast")
     public void testPaymentSuccess() throws Exception {
         setUp(PaymentPluginStatus.PROCESSED);
 
-        Assert.assertNull(paymentStateContext.getPaymentInfoPlugin());
+        Assert.assertNull(paymentStateContext.getPaymentTransactionInfoPlugin());
 
         Assert.assertEquals(paymentOperation.doOperationCallback(), OperationResult.SUCCESS);
 
-        Assert.assertNotNull(paymentStateContext.getPaymentInfoPlugin());
+        Assert.assertNotNull(paymentStateContext.getPaymentTransactionInfoPlugin());
     }
 
     private void setUp(final PaymentPluginStatus paymentPluginStatus) throws Exception {
diff --git a/payment/src/test/java/org/killbill/billing/payment/core/sm/TestPluginOperation.java b/payment/src/test/java/org/killbill/billing/payment/core/sm/TestPluginOperation.java
index fbf4a8c..f004f02 100644
--- a/payment/src/test/java/org/killbill/billing/payment/core/sm/TestPluginOperation.java
+++ b/payment/src/test/java/org/killbill/billing/payment/core/sm/TestPluginOperation.java
@@ -37,6 +37,7 @@ import org.killbill.billing.payment.api.PaymentApiException;
 import org.killbill.billing.payment.api.PluginProperty;
 import org.killbill.billing.payment.api.TransactionType;
 import org.killbill.billing.payment.core.ProcessorBase.WithAccountLockCallback;
+import org.killbill.billing.payment.core.sm.payments.PaymentOperation;
 import org.killbill.billing.payment.dispatcher.PluginDispatcher;
 import org.killbill.billing.payment.dispatcher.PluginDispatcher.PluginDispatcherReturnType;
 import org.killbill.billing.payment.plugin.api.PaymentPluginApiException;
@@ -81,7 +82,7 @@ public class TestPluginOperation extends PaymentTestSuiteNoDB {
             pluginOperation.dispatchWithAccountLockAndTimeout(callback);
             Assert.fail();
         } catch (final OperationException e) {
-            Assert.assertEquals(e.getOperationResult(), OperationResult.FAILURE);
+            Assert.assertEquals(e.getOperationResult(), OperationResult.EXCEPTION);
             Assert.assertTrue(e.getCause() instanceof PaymentApiException);
         }
     }
diff --git a/payment/src/test/java/org/killbill/billing/payment/core/sm/TestRetryablePayment.java b/payment/src/test/java/org/killbill/billing/payment/core/sm/TestRetryablePayment.java
index fffb976..fa4ef60 100644
--- a/payment/src/test/java/org/killbill/billing/payment/core/sm/TestRetryablePayment.java
+++ b/payment/src/test/java/org/killbill/billing/payment/core/sm/TestRetryablePayment.java
@@ -41,6 +41,7 @@ import org.killbill.billing.payment.api.TransactionStatus;
 import org.killbill.billing.payment.api.TransactionType;
 import org.killbill.billing.payment.core.PaymentProcessor;
 import org.killbill.billing.payment.core.PluginRoutingPaymentProcessor;
+import org.killbill.billing.payment.core.sm.control.PaymentStateControlContext;
 import org.killbill.billing.payment.dao.MockPaymentDao;
 import org.killbill.billing.payment.dao.PaymentAttemptModelDao;
 import org.killbill.billing.payment.dao.PaymentDao;
@@ -106,7 +107,7 @@ public class TestRetryablePayment extends PaymentTestSuiteNoDB {
     @Inject
     private PaymentStateMachineHelper paymentSMHelper;
     @Inject
-    private RetryStateMachineHelper retrySMHelper;
+    private PaymentControlStateMachineHelper retrySMHelper;
     @Inject
     private InternalCallContextFactory internalCallContextFactory;
 
@@ -123,7 +124,7 @@ public class TestRetryablePayment extends PaymentTestSuiteNoDB {
 
     private byte[] EMPTY_PROPERTIES;
     private MockRetryablePaymentAutomatonRunner runner;
-    private RetryablePaymentStateContext paymentStateContext;
+    private PaymentStateControlContext paymentStateContext;
     private MockRetryAuthorizeOperationCallback mockRetryAuthorizeOperationCallback;
     private PluginRoutingPaymentProcessor processor;
 
@@ -171,7 +172,7 @@ public class TestRetryablePayment extends PaymentTestSuiteNoDB {
                 eventBus);
 
         paymentStateContext =
-                new RetryablePaymentStateContext(ImmutableList.<String>of(MockPaymentRoutingProviderPlugin.PLUGIN_NAME),
+                new PaymentStateControlContext(ImmutableList.<String>of(MockPaymentRoutingProviderPlugin.PLUGIN_NAME),
                                                  true,
                                                  null,
                                                  paymentExternalKey,
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 836d2d3..21aee20 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
@@ -64,15 +64,18 @@ public class MockPaymentDao implements PaymentDao {
     }
 
     @Override
-    public int failOldPendingTransactions(final TransactionStatus newTransactionStatus, final DateTime createdBeforeDate, final InternalCallContext context) {
-        int result = 0;
-        synchronized (transactions) {
-            for (PaymentTransactionModelDao cur : transactions.values()) {
-                cur.setTransactionStatus(newTransactionStatus);
-                result++;
+    public List<PaymentTransactionModelDao> getByTransactionStatusAcrossTenants(final Iterable<TransactionStatus> transactionStatuses, DateTime createdBeforeDate, DateTime createdAfterDate, int limit) {
+        return ImmutableList.copyOf(Iterables.filter(transactions.values(), new Predicate<PaymentTransactionModelDao>() {
+            @Override
+            public boolean apply(final PaymentTransactionModelDao input) {
+                return Iterables.any(transactionStatuses, new Predicate<TransactionStatus>() {
+                    @Override
+                    public boolean apply(final TransactionStatus transactionStatus) {
+                        return input.getTransactionStatus() == transactionStatus;
+                    }
+                });
             }
-        }
-        return result;
+        }));
     }
 
     @Override
@@ -105,7 +108,7 @@ public class MockPaymentDao implements PaymentDao {
     }
 
     @Override
-    public List<PaymentAttemptModelDao> getPaymentAttemptsByState(final String stateName, final DateTime createdBeforeDate, final InternalTenantContext context) {
+    public List<PaymentAttemptModelDao> getPaymentAttemptsByStateAcrossTenants(final String stateName, final DateTime createdBeforeDate) {
         return null;
     }
 
@@ -244,7 +247,7 @@ public class MockPaymentDao implements PaymentDao {
     }
 
     @Override
-    public List<PaymentModelDao> getPaymentsByStates(final String[] states, final DateTime createdBeforeDate, final DateTime createdAfterDate, final int limit, final InternalTenantContext context) {
+    public List<PaymentModelDao> getPaymentsByStatesAcrossTenants(final String[] states, final DateTime createdBeforeDate, final DateTime createdAfterDate, final int limit) {
         return null;
     }
 
diff --git a/payment/src/test/java/org/killbill/billing/payment/dao/TestPaymentDao.java b/payment/src/test/java/org/killbill/billing/payment/dao/TestPaymentDao.java
index 67679de..358cf2b 100644
--- a/payment/src/test/java/org/killbill/billing/payment/dao/TestPaymentDao.java
+++ b/payment/src/test/java/org/killbill/billing/payment/dao/TestPaymentDao.java
@@ -259,7 +259,7 @@ public class TestPaymentDao extends PaymentTestSuiteWithEmbeddedDB {
         final String transactionExternalKey3 = "transaction3";
         final String transactionExternalKey4 = "transaction4";
 
-        final DateTime initialTime = clock.getUTCNow();
+        final DateTime initialTime = clock.getUTCNow().minusMinutes(1);
 
         final PaymentModelDao paymentModelDao = new PaymentModelDao(initialTime, initialTime, accountId, paymentMethodId, externalKey);
         final PaymentTransactionModelDao transaction1 = new PaymentTransactionModelDao(initialTime, initialTime, null, transactionExternalKey1,
@@ -267,7 +267,7 @@ public class TestPaymentDao extends PaymentTestSuiteWithEmbeddedDB {
                                                                                        TransactionStatus.PENDING, BigDecimal.TEN, Currency.AED,
                                                                                        "pending", "");
 
-        paymentDao.insertPaymentWithFirstTransaction(paymentModelDao, transaction1, internalCallContext);
+        final PaymentModelDao payment  = paymentDao.insertPaymentWithFirstTransaction(paymentModelDao, transaction1, internalCallContext);
 
         final PaymentTransactionModelDao transaction2 = new PaymentTransactionModelDao(initialTime, initialTime, null, transactionExternalKey2,
                                                                                        paymentModelDao.getId(), TransactionType.AUTHORIZE, initialTime,
@@ -284,7 +284,6 @@ public class TestPaymentDao extends PaymentTestSuiteWithEmbeddedDB {
 
         clock.addDays(1);
         final DateTime newTime = clock.getUTCNow();
-
         final InternalCallContext internalCallContextWithNewTime = new InternalCallContext(InternalCallContextFactory.INTERNAL_TENANT_RECORD_ID, 1687L, UUID.randomUUID(),
                                                                                            UUID.randomUUID().toString(), CallOrigin.TEST,
                                                                                            UserType.TEST, "Testing", "This is a test",
@@ -299,7 +298,13 @@ public class TestPaymentDao extends PaymentTestSuiteWithEmbeddedDB {
         final List<PaymentTransactionModelDao> result = getPendingTransactions(paymentModelDao.getId());
         Assert.assertEquals(result.size(), 3);
 
-        paymentDao.failOldPendingTransactions(TransactionStatus.PAYMENT_FAILURE, newTime, internalCallContext);
+        final List<PaymentTransactionModelDao> transactions1 = paymentDao.getByTransactionStatusAcrossTenants(ImmutableList.of(TransactionStatus.PENDING), newTime, initialTime, 3);
+        for (PaymentTransactionModelDao paymentTransaction : transactions1) {
+            final String newPaymentState = "XXX_FAILED";
+            paymentDao.updatePaymentAndTransactionOnCompletion(payment.getAccountId(), payment.getId(), paymentTransaction.getTransactionType(), newPaymentState, payment.getLastSuccessStateName(),
+                                                               paymentTransaction.getId(), TransactionStatus.PAYMENT_FAILURE, paymentTransaction.getProcessedAmount(), paymentTransaction.getProcessedCurrency(),
+                                                               paymentTransaction.getGatewayErrorCode(), paymentTransaction.getGatewayErrorMsg(), internalCallContext);
+        }
 
         final List<PaymentTransactionModelDao> result2 = getPendingTransactions(paymentModelDao.getId());
         Assert.assertEquals(result2.size(), 1);
@@ -311,7 +316,13 @@ public class TestPaymentDao extends PaymentTestSuiteWithEmbeddedDB {
         }
         ;
 
-        paymentDao.failOldPendingTransactions(TransactionStatus.PAYMENT_FAILURE, clock.getUTCNow(), internalCallContextWithNewTime);
+        final List<PaymentTransactionModelDao> transactions2 = paymentDao.getByTransactionStatusAcrossTenants(ImmutableList.of(TransactionStatus.PENDING), clock.getUTCNow(), initialTime, 1);
+        for (PaymentTransactionModelDao paymentTransaction : transactions2) {
+            final String newPaymentState = "XXX_FAILED";
+            paymentDao.updatePaymentAndTransactionOnCompletion(payment.getAccountId(), payment.getId(), paymentTransaction.getTransactionType(), newPaymentState, payment.getLastSuccessStateName(),
+                                                               paymentTransaction.getId(), TransactionStatus.PAYMENT_FAILURE, paymentTransaction.getProcessedAmount(), paymentTransaction.getProcessedCurrency(),
+                                                               paymentTransaction.getGatewayErrorCode(), paymentTransaction.getGatewayErrorMsg(), internalCallContext);
+        }
 
         final List<PaymentTransactionModelDao> result3 = getPendingTransactions(paymentModelDao.getId());
         Assert.assertEquals(result3.size(), 0);
@@ -319,7 +330,7 @@ public class TestPaymentDao extends PaymentTestSuiteWithEmbeddedDB {
     }
 
     @Test(groups = "slow")
-    public void testPaymentByStates() {
+    public void testPaymentByStatesAcrossTenants() {
 
         final UUID paymentMethodId = UUID.randomUUID();
         final UUID accountId = UUID.randomUUID();
@@ -350,8 +361,8 @@ public class TestPaymentDao extends PaymentTestSuiteWithEmbeddedDB {
                                                                                        TransactionStatus.UNKNOWN, BigDecimal.TEN, Currency.AED,
                                                                                        "unknown", "");
 
-        final InternalCallContext context1 = new InternalCallContext(internalCallContext.getTenantRecordId(),
-                                                                     internalCallContext.getAccountRecordId(),
+        final InternalCallContext context1 = new InternalCallContext(1L,
+                                                                     1L,
                                                                      internalCallContext.getUserToken(),
                                                                      internalCallContext.getCreatedBy(),
                                                                      internalCallContext.getCallOrigin(),
@@ -372,8 +383,8 @@ public class TestPaymentDao extends PaymentTestSuiteWithEmbeddedDB {
                                                                                        TransactionStatus.UNKNOWN, BigDecimal.TEN, Currency.AED,
                                                                                        "unknown", "");
 
-        final InternalCallContext context2 = new InternalCallContext(internalCallContext.getTenantRecordId(),
-                                                                     internalCallContext.getAccountRecordId(),
+        final InternalCallContext context2 = new InternalCallContext(2L,
+                                                                     2L,
                                                                      internalCallContext.getUserToken(),
                                                                      internalCallContext.getCreatedBy(),
                                                                      internalCallContext.getCallOrigin(),
@@ -393,8 +404,8 @@ public class TestPaymentDao extends PaymentTestSuiteWithEmbeddedDB {
                                                                                        TransactionStatus.UNKNOWN, BigDecimal.TEN, Currency.AED,
                                                                                        "unknown", "");
 
-        final InternalCallContext context3 = new InternalCallContext(internalCallContext.getTenantRecordId(),
-                                                                     internalCallContext.getAccountRecordId(),
+        final InternalCallContext context3 = new InternalCallContext(3L,
+                                                                     3L,
                                                                      internalCallContext.getUserToken(),
                                                                      internalCallContext.getCreatedBy(),
                                                                      internalCallContext.getCallOrigin(),
@@ -416,8 +427,8 @@ public class TestPaymentDao extends PaymentTestSuiteWithEmbeddedDB {
                                                                                        TransactionStatus.UNKNOWN, BigDecimal.TEN, Currency.AED,
                                                                                        "unknown", "");
 
-        final InternalCallContext context4 = new InternalCallContext(internalCallContext.getTenantRecordId(),
-                                                                     internalCallContext.getAccountRecordId(),
+        final InternalCallContext context4 = new InternalCallContext(4L,
+                                                                     4L,
                                                                      internalCallContext.getUserToken(),
                                                                      internalCallContext.getCreatedBy(),
                                                                      internalCallContext.getCallOrigin(),
@@ -438,8 +449,8 @@ public class TestPaymentDao extends PaymentTestSuiteWithEmbeddedDB {
                                                                                        TransactionStatus.UNKNOWN, BigDecimal.TEN, Currency.AED,
                                                                                        "unknown", "");
 
-        final InternalCallContext context5 = new InternalCallContext(internalCallContext.getTenantRecordId(),
-                                                                     internalCallContext.getAccountRecordId(),
+        final InternalCallContext context5 = new InternalCallContext(5L,
+                                                                     5L,
                                                                      internalCallContext.getUserToken(),
                                                                      internalCallContext.getCreatedBy(),
                                                                      internalCallContext.getCallOrigin(),
@@ -452,10 +463,67 @@ public class TestPaymentDao extends PaymentTestSuiteWithEmbeddedDB {
         paymentDao.insertPaymentWithFirstTransaction(paymentModelDao5, transaction5, context5);
 
         final String[] errorStates = {"AUTH_ERRORED", "CAPTURE_ERRORED", "REFUND_ERRORED", "CREDIT_ERRORED"};
-        final List<PaymentModelDao> result = paymentDao.getPaymentsByStates(errorStates, createdBeforeDate, createdAfterDate, 10, internalCallContext);
+        final List<PaymentModelDao> result = paymentDao.getPaymentsByStatesAcrossTenants(errorStates, createdBeforeDate, createdAfterDate, 10);
         assertEquals(result.size(), 2);
     }
 
+    @Test(groups = "slow")
+    public void testPaymentAttemptsByStateAcrossTenants() {
+
+        final UUID paymentMethodId = UUID.randomUUID();
+        final UUID accountId = UUID.randomUUID();
+        final String externalKey1 = "gfhfg";
+        final String transactionExternalKey1 = "sadas";
+
+        final String externalKey2 = "asdwqeqw";
+        final String transactionExternalKey2 = "fghfg";
+
+        final DateTime createdAfterDate = clock.getUTCNow().minusDays(10);
+        final DateTime createdBeforeDate = clock.getUTCNow().minusDays(1);
+
+        final String stateName = "FOO";
+        final String pluginName = "miraculous";
+
+        final PaymentAttemptModelDao attempt1 = new PaymentAttemptModelDao(accountId, paymentMethodId, createdAfterDate, createdAfterDate, externalKey1,
+                                                                     UUID.randomUUID(), transactionExternalKey1, TransactionType.AUTHORIZE, stateName, BigDecimal.ONE, Currency.USD,
+                                                                     ImmutableList.<String>of(pluginName), null);
+
+
+        final PaymentAttemptModelDao attempt2 = new PaymentAttemptModelDao(accountId, paymentMethodId, createdAfterDate, createdAfterDate, externalKey2,
+                                                                     UUID.randomUUID(), transactionExternalKey2, TransactionType.AUTHORIZE, stateName, BigDecimal.ONE, Currency.USD,
+                                                                     ImmutableList.<String>of(pluginName), null);
+
+        final InternalCallContext context1 = new InternalCallContext(1L,
+                                                                     1L,
+                                                                     internalCallContext.getUserToken(),
+                                                                     internalCallContext.getCreatedBy(),
+                                                                     internalCallContext.getCallOrigin(),
+                                                                     internalCallContext.getContextUserType(),
+                                                                     internalCallContext.getReasonCode(),
+                                                                     internalCallContext.getComments(),
+                                                                     createdAfterDate,
+                                                                     createdAfterDate);
+        paymentDao.insertPaymentAttemptWithProperties(attempt1, context1);
+
+
+        final InternalCallContext context2 = new InternalCallContext(2L,
+                                                                     2L,
+                                                                     internalCallContext.getUserToken(),
+                                                                     internalCallContext.getCreatedBy(),
+                                                                     internalCallContext.getCallOrigin(),
+                                                                     internalCallContext.getContextUserType(),
+                                                                     internalCallContext.getReasonCode(),
+                                                                     internalCallContext.getComments(),
+                                                                     createdAfterDate,
+                                                                     createdAfterDate);
+        paymentDao.insertPaymentAttemptWithProperties(attempt2, context2);
+
+
+        final List<PaymentAttemptModelDao> result = paymentDao.getPaymentAttemptsByStateAcrossTenants(stateName, createdBeforeDate);
+        Assert.assertEquals(result.size(), 2);
+    }
+
+
     private List<PaymentTransactionModelDao> getPendingTransactions(final UUID paymentId) {
         final List<PaymentTransactionModelDao> total = paymentDao.getTransactionsForPayment(paymentId, internalCallContext);
         return ImmutableList.copyOf(Iterables.filter(total, new Predicate<PaymentTransactionModelDao>() {
diff --git a/payment/src/test/java/org/killbill/billing/payment/PaymentTestSuiteWithEmbeddedDB.java b/payment/src/test/java/org/killbill/billing/payment/PaymentTestSuiteWithEmbeddedDB.java
index 3d144dc..81c4813 100644
--- a/payment/src/test/java/org/killbill/billing/payment/PaymentTestSuiteWithEmbeddedDB.java
+++ b/payment/src/test/java/org/killbill/billing/payment/PaymentTestSuiteWithEmbeddedDB.java
@@ -86,6 +86,7 @@ public abstract class PaymentTestSuiteWithEmbeddedDB extends GuicyKillbillTestSu
         super.beforeMethod();
         eventBus.start();
         Profiling.resetPerThreadProfilingData();
+        clock.resetDeltaFromReality();
 
     }
 
diff --git a/payment/src/test/java/org/killbill/billing/payment/provider/MockPaymentProviderPlugin.java b/payment/src/test/java/org/killbill/billing/payment/provider/MockPaymentProviderPlugin.java
index 28f90ed..f161297 100644
--- a/payment/src/test/java/org/killbill/billing/payment/provider/MockPaymentProviderPlugin.java
+++ b/payment/src/test/java/org/killbill/billing/payment/provider/MockPaymentProviderPlugin.java
@@ -19,6 +19,7 @@
 package org.killbill.billing.payment.provider;
 
 import java.math.BigDecimal;
+import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
 import java.util.UUID;
@@ -61,6 +62,8 @@ public class MockPaymentProviderPlugin implements NoOpPaymentPluginApi {
     private final AtomicBoolean makeAllInvoicesFailWithError = new AtomicBoolean(false);
 
     private final Map<String, InternalPaymentInfo> payments = new ConcurrentHashMap<String, InternalPaymentInfo>();
+    private final Map<String, List<PaymentTransactionInfoPlugin>> paymentTransactions = new ConcurrentHashMap<String, List<PaymentTransactionInfoPlugin>>();
+
     // Note: we can't use HashMultiMap as we care about storing duplicate key/value pairs
     private final Map<String, PaymentMethodPlugin> paymentMethods = new ConcurrentHashMap<String, PaymentMethodPlugin>();
     private final Map<String, PaymentMethodInfoPlugin> paymentMethodsInfo = new ConcurrentHashMap<String, PaymentMethodInfoPlugin>();
@@ -183,6 +186,7 @@ public class MockPaymentProviderPlugin implements NoOpPaymentPluginApi {
         makeNextInvoiceFailWithError.set(false);
         paymentMethods.clear();
         payments.clear();
+        paymentTransactions.clear();
         paymentMethodsInfo.clear();
     }
 
@@ -232,14 +236,8 @@ public class MockPaymentProviderPlugin implements NoOpPaymentPluginApi {
 
     @Override
     public List<PaymentTransactionInfoPlugin> getPaymentInfo(final UUID kbAccountId, final UUID kbPaymentId, final Iterable<PluginProperty> properties, final TenantContext context) throws PaymentPluginApiException {
-        /*
-        final InternalPaymentInfo paymentInfo = payments.get(kbPaymentId.toString());
-        if (paymentInfo == null) {
-            throw new PaymentPluginApiException("", "No payment found for payment id " + kbPaymentId.toString());
-        }
-        */
-        // Can't be implemented because we did not keep transaction details.
-        return ImmutableList.<PaymentTransactionInfoPlugin>of();
+        final List<PaymentTransactionInfoPlugin> result = paymentTransactions.get(kbPaymentId.toString());
+        return result != null ? result : ImmutableList.<PaymentTransactionInfoPlugin>of();
     }
 
     @Override
@@ -346,6 +344,13 @@ public class MockPaymentProviderPlugin implements NoOpPaymentPluginApi {
         info.addAmount(type, amount);
 
         final PaymentTransactionInfoPlugin result = new DefaultNoOpPaymentInfoPlugin(kbPaymentId, kbTransactionId, type, amount, currency, clock.getUTCNow(), clock.getUTCNow(), status, null);
+        List<PaymentTransactionInfoPlugin> existingTransactions = paymentTransactions.get(kbPaymentId.toString());
+        if (existingTransactions == null) {
+            existingTransactions = new ArrayList<PaymentTransactionInfoPlugin>();
+            paymentTransactions.put(kbPaymentId.toString(), existingTransactions);
+        }
+
+        existingTransactions.add(result);
         return result;
     }
 }
diff --git a/payment/src/test/java/org/killbill/billing/payment/provider/MockPaymentRoutingProviderPlugin.java b/payment/src/test/java/org/killbill/billing/payment/provider/MockPaymentRoutingProviderPlugin.java
index 5c72c72..7e9ff9b 100644
--- a/payment/src/test/java/org/killbill/billing/payment/provider/MockPaymentRoutingProviderPlugin.java
+++ b/payment/src/test/java/org/killbill/billing/payment/provider/MockPaymentRoutingProviderPlugin.java
@@ -1,7 +1,8 @@
 /*
- * Copyright 2014 Groupon, Inc
+ * Copyright 2014-2015 Groupon, Inc
+ * Copyright 2014-2015 The Billing Project, LLC
  *
- * Groupon licenses this file to you under the Apache License, version 2.0
+ * The Billing Project licenses this file to you under the Apache License, version 2.0
  * (the "License"); you may not use this file except in compliance with the
  * License.  You may obtain a copy of the License at:
  *
@@ -45,17 +46,17 @@ public class MockPaymentRoutingProviderPlugin implements PaymentRoutingPluginApi
     }
 
     @Override
-    public PriorPaymentRoutingResult priorCall(final PaymentRoutingContext paymentControlContext, Iterable<PluginProperty> properties) throws PaymentRoutingApiException {
-        return new DefaultPriorPaymentRoutingResult(isAborted, null, null, null);
+    public PriorPaymentRoutingResult priorCall(final PaymentRoutingContext paymentControlContext, final Iterable<PluginProperty> properties) throws PaymentRoutingApiException {
+        return new DefaultPriorPaymentRoutingResult(isAborted);
     }
 
     @Override
-    public OnSuccessPaymentRoutingResult onSuccessCall(final PaymentRoutingContext paymentControlContext, Iterable<PluginProperty> properties) throws PaymentRoutingApiException {
+    public OnSuccessPaymentRoutingResult onSuccessCall(final PaymentRoutingContext paymentControlContext, final Iterable<PluginProperty> properties) throws PaymentRoutingApiException {
         return null;
     }
 
     @Override
-    public OnFailurePaymentRoutingResult onFailureCall(final PaymentRoutingContext paymentControlContext, Iterable<PluginProperty> properties) throws PaymentRoutingApiException {
+    public OnFailurePaymentRoutingResult onFailureCall(final PaymentRoutingContext paymentControlContext, final Iterable<PluginProperty> properties) throws PaymentRoutingApiException {
         return new DefaultFailureCallResult(nextRetryDate);
     }
 }
diff --git a/payment/src/test/java/org/killbill/billing/payment/TestJanitor.java b/payment/src/test/java/org/killbill/billing/payment/TestJanitor.java
index 425fada..986106b 100644
--- a/payment/src/test/java/org/killbill/billing/payment/TestJanitor.java
+++ b/payment/src/test/java/org/killbill/billing/payment/TestJanitor.java
@@ -30,18 +30,19 @@ import org.killbill.billing.invoice.api.Invoice;
 import org.killbill.billing.invoice.api.InvoiceApiException;
 import org.killbill.billing.invoice.api.InvoiceItem;
 import org.killbill.billing.payment.api.Payment;
-import org.killbill.billing.payment.api.PaymentTransaction;
 import org.killbill.billing.payment.api.PaymentApiException;
 import org.killbill.billing.payment.api.PaymentOptions;
+import org.killbill.billing.payment.api.PaymentTransaction;
 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.invoice.InvoicePaymentRoutingPluginApi;
 import org.killbill.billing.payment.core.janitor.Janitor;
 import org.killbill.billing.payment.dao.PaymentAttemptModelDao;
+import org.killbill.billing.payment.invoice.InvoicePaymentRoutingPluginApi;
 import org.killbill.billing.payment.provider.MockPaymentProviderPlugin;
 import org.killbill.billing.platform.api.KillbillConfigSource;
 import org.killbill.bus.api.PersistentBus.EventBusException;
+import org.testng.Assert;
 import org.testng.annotations.AfterClass;
 import org.testng.annotations.AfterMethod;
 import org.testng.annotations.BeforeClass;
@@ -68,7 +69,6 @@ public class TestJanitor extends PaymentTestSuiteWithEmbeddedDB {
         }
     };
 
-
     @Inject
     private Janitor janitor;
 
@@ -94,7 +94,6 @@ public class TestJanitor extends PaymentTestSuiteWithEmbeddedDB {
         janitor.stop();
     }
 
-
     @BeforeMethod(groups = "slow")
     public void beforeMethod() throws Exception {
         super.beforeMethod();
@@ -131,7 +130,7 @@ public class TestJanitor extends PaymentTestSuiteWithEmbeddedDB {
                                                             Currency.USD));
 
         final Payment payment = paymentApi.createPurchaseWithPaymentControl(account, account.getPaymentMethodId(), null, requestedAmount, Currency.USD, paymentExternalKey, transactionExternalKey,
-                                                                                  createPropertiesForInvoice(invoice), INVOICE_PAYMENT, callContext);
+                                                                            createPropertiesForInvoice(invoice), INVOICE_PAYMENT, callContext);
         assertEquals(payment.getTransactions().size(), 1);
         assertEquals(payment.getTransactions().get(0).getTransactionStatus(), TransactionStatus.SUCCESS);
         assertEquals(payment.getTransactions().get(0).getTransactionType(), TransactionType.PURCHASE);
@@ -148,7 +147,11 @@ public class TestJanitor extends PaymentTestSuiteWithEmbeddedDB {
         assertEquals(attempt2.getStateName(), "INIT");
 
         clock.addDays(1);
-        try { Thread.sleep(1500); } catch (InterruptedException e) {};
+        try {
+            Thread.sleep(1500);
+        } catch (InterruptedException e) {
+        }
+        ;
 
         final PaymentAttemptModelDao attempt3 = paymentDao.getPaymentAttempt(attempt.getId(), internalCallContext);
         assertEquals(attempt3.getStateName(), "SUCCESS");
@@ -180,7 +183,7 @@ public class TestJanitor extends PaymentTestSuiteWithEmbeddedDB {
         invoice.addInvoiceItem(invoiceItem);
 
         final Payment payment = paymentApi.createPurchaseWithPaymentControl(account, account.getPaymentMethodId(), null, requestedAmount, Currency.USD, paymentExternalKey, transactionExternalKey,
-                                                                                  createPropertiesForInvoice(invoice), INVOICE_PAYMENT, callContext);
+                                                                            createPropertiesForInvoice(invoice), INVOICE_PAYMENT, callContext);
 
         final List<PluginProperty> refundProperties = new ArrayList<PluginProperty>();
         final HashMap<UUID, BigDecimal> uuidBigDecimalHashMap = new HashMap<UUID, BigDecimal>();
@@ -189,7 +192,7 @@ public class TestJanitor extends PaymentTestSuiteWithEmbeddedDB {
         refundProperties.add(refundIdsProp);
 
         final Payment payment2 = paymentApi.createRefundWithPaymentControl(account, payment.getId(), null, Currency.USD, transactionExternalKey2,
-                                                                                 refundProperties, INVOICE_PAYMENT, callContext);
+                                                                           refundProperties, INVOICE_PAYMENT, callContext);
 
         assertEquals(payment2.getTransactions().size(), 2);
         PaymentTransaction refundTransaction = payment2.getTransactions().get(1);
@@ -207,15 +210,71 @@ public class TestJanitor extends PaymentTestSuiteWithEmbeddedDB {
         assertEquals(attempt2.getStateName(), "INIT");
 
         clock.addDays(1);
-        try { Thread.sleep(1500); } catch (InterruptedException e) {};
+        try {
+            Thread.sleep(1500);
+        } catch (InterruptedException e) {
+        }
+        ;
 
         final PaymentAttemptModelDao attempt3 = paymentDao.getPaymentAttempt(refundAttempt.getId(), internalCallContext);
         assertEquals(attempt3.getStateName(), "SUCCESS");
+    }
+
+    @Test(groups = "slow")
+    public void testUnknownEntries() throws PaymentApiException, InvoiceApiException, EventBusException {
+
+        final BigDecimal requestedAmount = BigDecimal.TEN;
+        final String paymentExternalKey = "qwru";
+        final String transactionExternalKey = "lkjdsf";
+
+        final Payment payment = paymentApi.createAuthorization(account, account.getPaymentMethodId(), null, requestedAmount, account.getCurrency(), paymentExternalKey,
+                                                               transactionExternalKey, ImmutableList.<PluginProperty>of(), callContext);
+
+        // Artificially move the transaction status to UNKNOWN
+        final String paymentStateName = paymentSMHelper.getErroredStateForTransaction(TransactionType.AUTHORIZE).toString();
+        paymentDao.updatePaymentAndTransactionOnCompletion(account.getId(), payment.getId(), TransactionType.AUTHORIZE, paymentStateName, paymentStateName,
+                                                           payment.getTransactions().get(0).getId(), TransactionStatus.UNKNOWN, requestedAmount, account.getCurrency(),
+                                                           "foo", "bar", internalCallContext);
+        // The UnknownPaymentTransactionTask will look for UNKNOWN payment that *just happened* , and that are not too old (less than 7 days)
+        clock.addDays(1);
+        try {
+            Thread.sleep(1500);
+        } catch (InterruptedException e) {
+        }
+
+        final Payment updatedPayment = paymentApi.getPayment(payment.getId(), false, ImmutableList.<PluginProperty>of(), callContext);
+        assertEquals(updatedPayment.getTransactions().get(0).getTransactionStatus(), TransactionStatus.SUCCESS);
 
     }
 
 
 
+    @Test(groups = "slow")
+    public void testPendingEntries() throws PaymentApiException, InvoiceApiException, EventBusException {
+
+        final BigDecimal requestedAmount = BigDecimal.TEN;
+        final String paymentExternalKey = "jhj44";
+        final String transactionExternalKey = "4jhjj2";
+
+        final Payment payment = paymentApi.createAuthorization(account, account.getPaymentMethodId(), null, requestedAmount, account.getCurrency(), paymentExternalKey,
+                                                               transactionExternalKey, ImmutableList.<PluginProperty>of(), callContext);
+
+        // Artificially move the transaction status to PENDING
+        final String paymentStateName = paymentSMHelper.getPendingStateForTransaction(TransactionType.AUTHORIZE).toString();
+        paymentDao.updatePaymentAndTransactionOnCompletion(account.getId(), payment.getId(), TransactionType.AUTHORIZE, paymentStateName, paymentStateName,
+                                                           payment.getTransactions().get(0).getId(), TransactionStatus.PENDING, requestedAmount, account.getCurrency(),
+                                                           "loup", "chat", internalCallContext);
+        clock.addDays(1);
+        try {
+            Thread.sleep(1500);
+        } catch (InterruptedException e) {
+        }
+
+        final Payment updatedPayment = paymentApi.getPayment(payment.getId(), false, ImmutableList.<PluginProperty>of(), callContext);
+        Assert.assertEquals(updatedPayment.getTransactions().get(0).getTransactionStatus(), TransactionStatus.SUCCESS);
+
+    }
+
     private List<PluginProperty> createPropertiesForInvoice(final Invoice invoice) {
         final List<PluginProperty> result = new ArrayList<PluginProperty>();
         result.add(new PluginProperty(InvoicePaymentRoutingPluginApi.PROP_IPCD_INVOICE_ID, invoice.getId().toString(), false));

pom.xml 2(+1 -1)

diff --git a/pom.xml b/pom.xml
index 67c26f2..ffe30ae 100644
--- a/pom.xml
+++ b/pom.xml
@@ -21,7 +21,7 @@
     <parent>
         <artifactId>killbill-oss-parent</artifactId>
         <groupId>org.kill-bill.billing</groupId>
-        <version>0.12</version>
+        <version>0.14</version>
     </parent>
     <artifactId>killbill</artifactId>
     <version>0.15.0-SNAPSHOT</version>
diff --git a/profiles/killbill/src/main/java/org/killbill/billing/server/log/obfuscators/ConfigMagicObfuscator.java b/profiles/killbill/src/main/java/org/killbill/billing/server/log/obfuscators/ConfigMagicObfuscator.java
index 6ecd7ce..e425aa3 100644
--- a/profiles/killbill/src/main/java/org/killbill/billing/server/log/obfuscators/ConfigMagicObfuscator.java
+++ b/profiles/killbill/src/main/java/org/killbill/billing/server/log/obfuscators/ConfigMagicObfuscator.java
@@ -21,6 +21,7 @@ import java.util.Collection;
 import java.util.LinkedList;
 import java.util.regex.Pattern;
 
+import ch.qos.logback.classic.spi.ILoggingEvent;
 import com.google.common.collect.ImmutableList;
 
 // See ConfigurationObjectFactory
@@ -48,8 +49,8 @@ public class ConfigMagicObfuscator extends Obfuscator {
     }
 
     @Override
-    public String obfuscate(final String originalString) {
-        return obfuscate(originalString, patterns);
+    public String obfuscate(final String originalString, final ILoggingEvent event) {
+        return obfuscate(originalString, patterns, event);
     }
 
     private Pattern buildPattern(final String key) {
diff --git a/profiles/killbill/src/main/java/org/killbill/billing/server/log/obfuscators/LuhnMaskingObfuscator.java b/profiles/killbill/src/main/java/org/killbill/billing/server/log/obfuscators/LuhnMaskingObfuscator.java
index 88bda32..1edb705 100644
--- a/profiles/killbill/src/main/java/org/killbill/billing/server/log/obfuscators/LuhnMaskingObfuscator.java
+++ b/profiles/killbill/src/main/java/org/killbill/billing/server/log/obfuscators/LuhnMaskingObfuscator.java
@@ -17,6 +17,7 @@
 
 package org.killbill.billing.server.log.obfuscators;
 
+import ch.qos.logback.classic.spi.ILoggingEvent;
 import com.google.common.annotations.VisibleForTesting;
 
 /**
@@ -38,7 +39,7 @@ public class LuhnMaskingObfuscator extends Obfuscator {
     }
 
     @Override
-    public String obfuscate(final String originalString) {
+    public String obfuscate(final String originalString, final ILoggingEvent event) {
         return mask(originalString);
     }
 
diff --git a/profiles/killbill/src/main/java/org/killbill/billing/server/log/obfuscators/Obfuscator.java b/profiles/killbill/src/main/java/org/killbill/billing/server/log/obfuscators/Obfuscator.java
index 68675e7..bf37cca 100644
--- a/profiles/killbill/src/main/java/org/killbill/billing/server/log/obfuscators/Obfuscator.java
+++ b/profiles/killbill/src/main/java/org/killbill/billing/server/log/obfuscators/Obfuscator.java
@@ -22,10 +22,16 @@ import java.util.regex.Pattern;
 
 import javax.annotation.Nullable;
 
+import org.killbill.commons.profiling.ProfilingFeature.ProfilingFeatureType;
+
+import ch.qos.logback.classic.spi.ILoggingEvent;
 import com.google.common.annotations.VisibleForTesting;
 
 public abstract class Obfuscator {
 
+    @VisibleForTesting
+    static final String LOGGING_FILTER_NAME = "com.sun.jersey.api.container.filter.LoggingFilter";
+
     protected static final int DEFAULT_PATTERN_FLAGS = Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL;
 
     protected static final String MASK_LABEL = "MASKED";
@@ -40,24 +46,28 @@ public abstract class Obfuscator {
         }
     }
 
-    public abstract String obfuscate(final String originalString);
+    public abstract String obfuscate(final String originalString, final ILoggingEvent event);
 
-    protected String obfuscate(final String originalString, final Iterable<Pattern> patterns) {
+    protected String obfuscate(final String originalString, final Iterable<Pattern> patterns, final ILoggingEvent event) {
         final StringBuilder obfuscatedStringBuilder = new StringBuilder(originalString);
 
         for (final Pattern pattern : patterns) {
             int currentOffset = 0;
             // Create a matcher with a copy of the current obfuscated String
-            Matcher matcher = pattern.matcher(obfuscatedStringBuilder.toString());
+            final Matcher matcher = pattern.matcher(obfuscatedStringBuilder.toString());
             while (matcher.find()) {
                 for (int groupNb = 1; groupNb <= matcher.groupCount(); groupNb++) {
                     final String confidentialData = matcher.group(groupNb);
-                    final String obfuscatedConfidentialData = obfuscateConfidentialData(confidentialData);
-                    obfuscatedStringBuilder.replace(currentOffset + matcher.start(groupNb), currentOffset + matcher.end(groupNb), obfuscatedConfidentialData);
 
-                    // The original String is modified in place, which will confuse the Matcher if it becomes bigger
-                    if (obfuscatedConfidentialData.length() > confidentialData.length()) {
-                        currentOffset += obfuscatedConfidentialData.length() - confidentialData.length();
+                    if (shouldObfuscate(confidentialData, event)) {
+                        final String obfuscatedConfidentialData = obfuscateConfidentialData(confidentialData);
+
+                        obfuscatedStringBuilder.replace(currentOffset + matcher.start(groupNb), currentOffset + matcher.end(groupNb), obfuscatedConfidentialData);
+
+                        // The original String is modified in place, which will confuse the Matcher if it becomes bigger
+                        if (obfuscatedConfidentialData.length() > confidentialData.length()) {
+                            currentOffset += obfuscatedConfidentialData.length() - confidentialData.length();
+                        }
                     }
                 }
             }
@@ -66,6 +76,27 @@ public abstract class Obfuscator {
         return obfuscatedStringBuilder.toString();
     }
 
+    private boolean shouldObfuscate(final String confidentialData, final ILoggingEvent event) {
+        return !isProfilingHeader(confidentialData, event);
+    }
+
+    // Huge hack to avoid obfuscating the "name" in the X-Killbill-Profiling-Resp json. Unfortunately, we can't simply
+    // filter-out c.s.j.a.c.filter.LoggingFilter because we do want to obfuscate requests (in case sensitive data is passed as
+    // query parameters, e.g. in plugin properties)
+    private boolean isProfilingHeader(final String confidentialData, final ILoggingEvent event) {
+        if (!LOGGING_FILTER_NAME.equals(event.getLoggerName())) {
+            return false;
+        }
+
+        for (final ProfilingFeatureType profileType : ProfilingFeatureType.values()) {
+            // See ProfilingDataItem#getKey
+            if (confidentialData.startsWith("\"" + profileType.name() + ":")) {
+                return true;
+            }
+        }
+        return false;
+    }
+
     private String obfuscateConfidentialData(final CharSequence confidentialSequence) {
         return obfuscateConfidentialData(confidentialSequence, null);
     }
diff --git a/profiles/killbill/src/main/java/org/killbill/billing/server/log/obfuscators/ObfuscatorConverter.java b/profiles/killbill/src/main/java/org/killbill/billing/server/log/obfuscators/ObfuscatorConverter.java
index 2b5f9e5..39b9306 100644
--- a/profiles/killbill/src/main/java/org/killbill/billing/server/log/obfuscators/ObfuscatorConverter.java
+++ b/profiles/killbill/src/main/java/org/killbill/billing/server/log/obfuscators/ObfuscatorConverter.java
@@ -53,7 +53,7 @@ public class ObfuscatorConverter extends ClassicConverter {
         String convertedMessage = event.getFormattedMessage();
         for (final Obfuscator obfuscator : obfuscators) {
             try {
-                convertedMessage = obfuscator.obfuscate(convertedMessage);
+                convertedMessage = obfuscator.obfuscate(convertedMessage, event);
             } catch (final RuntimeException e) {
                 // Ignore? Not sure the impact of importing a logger here
             }
diff --git a/profiles/killbill/src/main/java/org/killbill/billing/server/log/obfuscators/PatternObfuscator.java b/profiles/killbill/src/main/java/org/killbill/billing/server/log/obfuscators/PatternObfuscator.java
index f9a38a5..bed40f9 100644
--- a/profiles/killbill/src/main/java/org/killbill/billing/server/log/obfuscators/PatternObfuscator.java
+++ b/profiles/killbill/src/main/java/org/killbill/billing/server/log/obfuscators/PatternObfuscator.java
@@ -21,6 +21,7 @@ import java.util.Collection;
 import java.util.LinkedList;
 import java.util.regex.Pattern;
 
+import ch.qos.logback.classic.spi.ILoggingEvent;
 import com.google.common.collect.ImmutableList;
 
 public class PatternObfuscator extends Obfuscator {
@@ -63,8 +64,8 @@ public class PatternObfuscator extends Obfuscator {
     }
 
     @Override
-    public String obfuscate(final String originalString) {
-        return obfuscate(originalString, patterns);
+    public String obfuscate(final String originalString, final ILoggingEvent event) {
+        return obfuscate(originalString, patterns, event);
     }
 
     private Pattern buildJSONPattern(final String key) {
@@ -72,7 +73,7 @@ public class PatternObfuscator extends Obfuscator {
     }
 
     private Pattern buildXMLPattern(final String key) {
-        return Pattern.compile(key + ">([^<\\n]+)", DEFAULT_PATTERN_FLAGS);
+        return Pattern.compile(key + ">([^<\\n]+)</[^<>]*" + key + ">", DEFAULT_PATTERN_FLAGS);
     }
 
     private Pattern buildMultiValuesXMLPattern(final String key) {
diff --git a/profiles/killbill/src/main/java/org/killbill/billing/server/modules/KillbillServerModule.java b/profiles/killbill/src/main/java/org/killbill/billing/server/modules/KillbillServerModule.java
index 3a97fb5..a67a7d5 100644
--- a/profiles/killbill/src/main/java/org/killbill/billing/server/modules/KillbillServerModule.java
+++ b/profiles/killbill/src/main/java/org/killbill/billing/server/modules/KillbillServerModule.java
@@ -27,6 +27,7 @@ import org.killbill.billing.currency.glue.CurrencyModule;
 import org.killbill.billing.entitlement.glue.DefaultEntitlementModule;
 import org.killbill.billing.invoice.glue.DefaultInvoiceModule;
 import org.killbill.billing.jaxrs.resources.AccountResource;
+import org.killbill.billing.jaxrs.resources.AdminResource;
 import org.killbill.billing.jaxrs.resources.BundleResource;
 import org.killbill.billing.jaxrs.resources.CatalogResource;
 import org.killbill.billing.jaxrs.resources.CreditResource;
@@ -184,6 +185,7 @@ public class KillbillServerModule extends KillbillPlatformModule {
         bind(TestResource.class).asEagerSingleton();
         bind(TransactionResource.class).asEagerSingleton();
         bind(UsageResource.class).asEagerSingleton();
+        bind(AdminResource.class).asEagerSingleton();
     }
 
     protected void configureFilters() {
diff --git a/profiles/killbill/src/test/java/org/killbill/billing/server/log/obfuscators/TestConfigMagicObfuscator.java b/profiles/killbill/src/test/java/org/killbill/billing/server/log/obfuscators/TestConfigMagicObfuscator.java
index 382783a..fb6d67e 100644
--- a/profiles/killbill/src/test/java/org/killbill/billing/server/log/obfuscators/TestConfigMagicObfuscator.java
+++ b/profiles/killbill/src/test/java/org/killbill/billing/server/log/obfuscators/TestConfigMagicObfuscator.java
@@ -18,9 +18,12 @@
 package org.killbill.billing.server.log.obfuscators;
 
 import org.killbill.billing.server.log.ServerTestSuiteNoDB;
+import org.mockito.Mockito;
 import org.testng.Assert;
 import org.testng.annotations.Test;
 
+import ch.qos.logback.classic.spi.ILoggingEvent;
+
 public class TestConfigMagicObfuscator extends ServerTestSuiteNoDB {
 
     private final ConfigMagicObfuscator obfuscator = new ConfigMagicObfuscator();
@@ -44,7 +47,7 @@ public class TestConfigMagicObfuscator extends ServerTestSuiteNoDB {
     }
 
     private void verify(final String input, final String output) {
-        final String obfuscated = obfuscator.obfuscate(input);
+        final String obfuscated = obfuscator.obfuscate(input, Mockito.mock(ILoggingEvent.class));
         Assert.assertEquals(obfuscated, output, obfuscated);
     }
 }
diff --git a/profiles/killbill/src/test/java/org/killbill/billing/server/log/obfuscators/TestLuhnMaskingObfuscator.java b/profiles/killbill/src/test/java/org/killbill/billing/server/log/obfuscators/TestLuhnMaskingObfuscator.java
index 5795287..794a96d 100644
--- a/profiles/killbill/src/test/java/org/killbill/billing/server/log/obfuscators/TestLuhnMaskingObfuscator.java
+++ b/profiles/killbill/src/test/java/org/killbill/billing/server/log/obfuscators/TestLuhnMaskingObfuscator.java
@@ -18,9 +18,12 @@
 package org.killbill.billing.server.log.obfuscators;
 
 import org.killbill.billing.server.log.ServerTestSuiteNoDB;
+import org.mockito.Mockito;
 import org.testng.Assert;
 import org.testng.annotations.Test;
 
+import ch.qos.logback.classic.spi.ILoggingEvent;
+
 public class TestLuhnMaskingObfuscator extends ServerTestSuiteNoDB {
 
     private final LuhnMaskingObfuscator obfuscator = new LuhnMaskingObfuscator();
@@ -212,8 +215,46 @@ public class TestLuhnMaskingObfuscator extends ServerTestSuiteNoDB {
                + "6331101999990017");
     }
 
+    @Test(groups = "fast")
+    public void testQueryParametersAreObfuscated() throws Exception {
+        final ILoggingEvent event = Mockito.mock(ILoggingEvent.class);
+        Mockito.when(event.getLoggerName()).thenReturn(Obfuscator.LOGGING_FILTER_NAME);
+
+        // Check the query parameters are obfuscated (see TestPatternObfuscator#testProfilingHeaderIsNotObfuscated)
+        // TODO Pierre: Obfuscate the Authorization and X-Killbill-ApiSecret headers?
+        verify("1 * Server in-bound request\n" +
+               "1 > POST http://127.0.0.1:8080/1.0/kb/accounts/2a55045a-ce1d-4344-942d-b825536328f9/payments?pluginProperty=cc_number=4111111111111111\n" +
+               "1 > X-Killbill-ApiSecret: lazar\n" +
+               "1 > Authorization: Basic YWRtaW46cGFzc3dvcmQ=\n" +
+               "1 > X-Killbill-CreatedBy: admin\n" +
+               "1 > Host: 127.0.0.1:8080\n" +
+               "1 > Content-Length: 62\n" +
+               "1 > User-Agent: curl/7.30.0\n" +
+               "1 > X-Killbill-Profiling-Req: DAO\n" +
+               "1 > X-Killbill-ApiKey: bob\n" +
+               "1 > Content-Type: application/json\n" +
+               "1 > Accept: */*",
+               "1 * Server in-bound request\n" +
+               "1 > POST http://127.0.0.1:8080/1.0/kb/accounts/2a55045a-ce1d-4344-942d-b825536328f9/payments?pluginProperty=cc_number=***MASKED***1111\n" +
+               "1 > X-Killbill-ApiSecret: lazar\n" +
+               "1 > Authorization: Basic YWRtaW46cGFzc3dvcmQ=\n" +
+               "1 > X-Killbill-CreatedBy: admin\n" +
+               "1 > Host: 127.0.0.1:8080\n" +
+               "1 > Content-Length: 62\n" +
+               "1 > User-Agent: curl/7.30.0\n" +
+               "1 > X-Killbill-Profiling-Req: DAO\n" +
+               "1 > X-Killbill-ApiKey: bob\n" +
+               "1 > Content-Type: application/json\n" +
+               "1 > Accept: */*",
+               event);
+    }
+
     private void verify(final String input, final String output) {
-        final String obfuscated = obfuscator.obfuscate(input);
+        verify(input, output, Mockito.mock(ILoggingEvent.class));
+    }
+
+    private void verify(final String input, final String output, final ILoggingEvent event) {
+        final String obfuscated = obfuscator.obfuscate(input, event);
         Assert.assertEquals(obfuscated, output, obfuscated);
     }
 }
diff --git a/profiles/killbill/src/test/java/org/killbill/billing/server/log/obfuscators/TestObfuscator.java b/profiles/killbill/src/test/java/org/killbill/billing/server/log/obfuscators/TestObfuscator.java
index 59bd626..5109ad3 100644
--- a/profiles/killbill/src/test/java/org/killbill/billing/server/log/obfuscators/TestObfuscator.java
+++ b/profiles/killbill/src/test/java/org/killbill/billing/server/log/obfuscators/TestObfuscator.java
@@ -20,16 +20,18 @@ package org.killbill.billing.server.log.obfuscators;
 import java.util.regex.Pattern;
 
 import org.killbill.billing.server.log.ServerTestSuiteNoDB;
+import org.mockito.Mockito;
 import org.testng.Assert;
 import org.testng.annotations.Test;
 
+import ch.qos.logback.classic.spi.ILoggingEvent;
 import com.google.common.collect.ImmutableList;
 
 public class TestObfuscator extends ServerTestSuiteNoDB {
 
     private final Obfuscator obfuscator = new Obfuscator() {
         @Override
-        public String obfuscate(final String originalString) {
+        public String obfuscate(final String originalString, final ILoggingEvent event) {
             return null;
         }
     };
@@ -38,7 +40,7 @@ public class TestObfuscator extends ServerTestSuiteNoDB {
     public void testObfuscateWithOnePattern() throws Exception {
         final Pattern pattern = Pattern.compile("number=([^;]+)");
         final ImmutableList<Pattern> patterns = ImmutableList.<Pattern>of(pattern);
-        Assert.assertEquals(obfuscator.obfuscate("number=1234;number=12345;number=123456;number=1234567;number=12345678;number=123456789", patterns),
+        Assert.assertEquals(obfuscator.obfuscate("number=1234;number=12345;number=123456;number=1234567;number=12345678;number=123456789", patterns, Mockito.mock(ILoggingEvent.class)),
                             "number=MASKED;number=MASKED;number=MASKED;number=MASKED*;number=*MASKED*;number=*MASKED**");
 
     }
@@ -48,7 +50,7 @@ public class TestObfuscator extends ServerTestSuiteNoDB {
         final Pattern pattern1 = Pattern.compile("number=([^;]+)");
         final Pattern pattern2 = Pattern.compile("numberB=([^;]+)");
         final ImmutableList<Pattern> patterns = ImmutableList.<Pattern>of(pattern1, pattern2);
-        Assert.assertEquals(obfuscator.obfuscate("number=1234;numberB=12345;number=123456;numberB=1234567;number=12345678;numberB=123456789", patterns),
+        Assert.assertEquals(obfuscator.obfuscate("number=1234;numberB=12345;number=123456;numberB=1234567;number=12345678;numberB=123456789", patterns, Mockito.mock(ILoggingEvent.class)),
                             "number=MASKED;numberB=MASKED;number=MASKED;numberB=MASKED*;number=*MASKED*;numberB=*MASKED**");
 
     }
diff --git a/profiles/killbill/src/test/java/org/killbill/billing/server/log/obfuscators/TestPatternObfuscator.java b/profiles/killbill/src/test/java/org/killbill/billing/server/log/obfuscators/TestPatternObfuscator.java
index 6147466..3476f58 100644
--- a/profiles/killbill/src/test/java/org/killbill/billing/server/log/obfuscators/TestPatternObfuscator.java
+++ b/profiles/killbill/src/test/java/org/killbill/billing/server/log/obfuscators/TestPatternObfuscator.java
@@ -18,9 +18,12 @@
 package org.killbill.billing.server.log.obfuscators;
 
 import org.killbill.billing.server.log.ServerTestSuiteNoDB;
+import org.mockito.Mockito;
 import org.testng.Assert;
 import org.testng.annotations.Test;
 
+import ch.qos.logback.classic.spi.ILoggingEvent;
+
 public class TestPatternObfuscator extends ServerTestSuiteNoDB {
 
     private final PatternObfuscator obfuscator = new PatternObfuscator();
@@ -257,8 +260,34 @@ public class TestPatternObfuscator extends ServerTestSuiteNoDB {
               );
     }
 
+    @Test(groups = "fast", description = "Test for ActiveMerchant wiredump_device logging")
+    public void testWithQuotedNewLines() throws Exception {
+        verify("[cybersource-plugin] \"<?xml version=\\\"1.0\\\" encoding=\\\"UTF-8\\\"?><accountNumber>4111111111111111</accountNumber>\\n  <expirationMonth>09</expirationMonth>\\n  \"",
+               "[cybersource-plugin] \"<?xml version=\\\"1.0\\\" encoding=\\\"UTF-8\\\"?><accountNumber>*****MASKED*****</accountNumber>\\n  <expirationMonth>09</expirationMonth>\\n  \"");
+    }
+
+    @Test(groups = "fast")
+    public void testProfilingHeaderIsNotObfuscated() throws Exception {
+        final ILoggingEvent event = Mockito.mock(ILoggingEvent.class);
+        Mockito.when(event.getLoggerName()).thenReturn(Obfuscator.LOGGING_FILTER_NAME);
+
+        verify("1 * Server out-bound response\n" +
+               "1 < 500\n" +
+               "1 < Content-Type: application/json\n" +
+               "1 < X-Killbill-Profiling-Resp: {\"rawData\":[{\"name\":\"DAO:AccountSqlDao:getById\",\"durationUsec\":14873},{\"name\":\"DAO:PaymentMethodSqlDao:getById\",\"durationUsec\":10438},{\"name\":\"DAO:PaymentSqlDao:create\",\"durationUsec\":31750},{\"name\":\"DAO:TransactionSqlDao:create\",\"durationUsec\":23121},{\"name\":\"DAO:PaymentSqlDao:getById\",\"durationUsec\":2541},{\"name\":\"DAO:TransactionSqlDao:getByPaymentId\",\"durationUsec\":3574},{\"name\":\"DAO:PaymentMethodSqlDao:getPaymentMethodIncludedDelete\",\"durationUsec\":1763},{\"name\":\"DAO:TransactionSqlDao:updateTransactionStatus\",\"durationUsec\":13994},{\"name\":\"DAO:PaymentSqlDao:updatePaymentStateName\",\"durationUsec\":11929},{\"name\":\"DAO:TransactionSqlDao:getById\",\"durationUsec\":5245}]}",
+               event);
+    }
+
+    private void verify(final String input, final ILoggingEvent event) {
+        verify(input, input, event);
+    }
+
     private void verify(final String input, final String output) {
-        final String obfuscated = obfuscator.obfuscate(input);
+        verify(input, output, Mockito.mock(ILoggingEvent.class));
+    }
+
+    private void verify(final String input, final String output, final ILoggingEvent event) {
+        final String obfuscated = obfuscator.obfuscate(input, event);
         Assert.assertEquals(obfuscated, output, obfuscated);
     }
 }
diff --git a/subscription/src/main/java/org/killbill/billing/subscription/api/migration/DefaultSubscriptionBaseMigrationApi.java b/subscription/src/main/java/org/killbill/billing/subscription/api/migration/DefaultSubscriptionBaseMigrationApi.java
index 218b7d7..39fdf2b 100644
--- a/subscription/src/main/java/org/killbill/billing/subscription/api/migration/DefaultSubscriptionBaseMigrationApi.java
+++ b/subscription/src/main/java/org/killbill/billing/subscription/api/migration/DefaultSubscriptionBaseMigrationApi.java
@@ -52,6 +52,7 @@ import org.killbill.billing.subscription.events.user.ApiEventMigrateBilling;
 import org.killbill.billing.subscription.events.user.ApiEventMigrateSubscription;
 import org.killbill.billing.subscription.events.user.ApiEventType;
 import org.killbill.billing.subscription.exceptions.SubscriptionBaseError;
+import org.killbill.billing.util.UUIDs;
 import org.killbill.billing.util.callcontext.CallContext;
 import org.killbill.billing.util.callcontext.InternalCallContextFactory;
 
@@ -147,7 +148,7 @@ public class DefaultSubscriptionBaseMigrationApi extends SubscriptionApiBase imp
         final DefaultSubscriptionBase defaultSubscriptionBase;
         try {
             defaultSubscriptionBase = createSubscriptionForApiUse(new SubscriptionBuilder()
-                                                                          .setId(UUID.randomUUID())
+                                                                          .setId(UUIDs.randomUUID())
                                                                           .setBundleId(bundleId)
                                                                           .setCategory(productCategory)
                                                                           .setBundleStartDate(migrationStartDate)
@@ -169,7 +170,7 @@ public class DefaultSubscriptionBaseMigrationApi extends SubscriptionApiBase imp
         final DefaultSubscriptionBase defaultSubscriptionBase;
         try {
             defaultSubscriptionBase = createSubscriptionForApiUse(new SubscriptionBuilder()
-                                                                                                        .setId(UUID.randomUUID())
+                                                                                                        .setId(UUIDs.randomUUID())
                                                                                                         .setBundleId(bundleId)
                                                                                                         .setCategory(productCategory)
                                                                                                         .setBundleStartDate(bundleStartDate)
@@ -242,7 +243,7 @@ public class DefaultSubscriptionBaseMigrationApi extends SubscriptionApiBase imp
             // create the MIGRATE_BILLING based on the current state of the last event.
             if (!cur.getEventTime().isAfter(ctd)) {
                 builder.setEffectiveDate(ctd);
-                builder.setUuid(UUID.randomUUID());
+                builder.setUuid(UUIDs.randomUUID());
                 apiEventMigrateBilling = new ApiEventMigrateBilling(builder);
             }
         }
diff --git a/subscription/src/main/java/org/killbill/billing/subscription/api/svcs/DefaultSubscriptionInternalApi.java b/subscription/src/main/java/org/killbill/billing/subscription/api/svcs/DefaultSubscriptionInternalApi.java
index c273969..5ec8723 100644
--- a/subscription/src/main/java/org/killbill/billing/subscription/api/svcs/DefaultSubscriptionInternalApi.java
+++ b/subscription/src/main/java/org/killbill/billing/subscription/api/svcs/DefaultSubscriptionInternalApi.java
@@ -70,6 +70,7 @@ import org.killbill.billing.subscription.engine.dao.model.SubscriptionBundleMode
 import org.killbill.billing.subscription.events.SubscriptionBaseEvent;
 import org.killbill.billing.subscription.events.SubscriptionBaseEvent.EventType;
 import org.killbill.billing.subscription.exceptions.SubscriptionBaseError;
+import org.killbill.billing.util.UUIDs;
 import org.killbill.billing.util.callcontext.CallContext;
 import org.killbill.billing.util.callcontext.InternalCallContextFactory;
 import org.killbill.billing.util.callcontext.TenantContext;
@@ -151,7 +152,7 @@ public class DefaultSubscriptionInternalApi extends SubscriptionApiBase implemen
             final DefaultSubscriptionBase baseSubscription = (DefaultSubscriptionBase) dao.getBaseSubscription(bundleId, context);
             final DateTime bundleStartDate = getBundleStartDateWithSanity(bundleId, baseSubscription, plan, requestedDate, effectiveDate, context);
             return apiService.createPlan(new SubscriptionBuilder()
-                                                 .setId(UUID.randomUUID())
+                                                 .setId(UUIDs.randomUUID())
                                                  .setBundleId(bundleId)
                                                  .setCategory(plan.getProduct().getCategory())
                                                  .setBundleStartDate(bundleStartDate)
@@ -432,7 +433,7 @@ public class DefaultSubscriptionInternalApi extends SubscriptionApiBase implemen
                         final DefaultSubscriptionBase baseSubscription = (DefaultSubscriptionBase) dao.getBaseSubscription(bundleId, context);
                         final DateTime startEffectiveDate = dryRunArguments.getEffectiveDate() != null ? dryRunArguments.getEffectiveDate() : utcNow;
                         final DateTime bundleStartDate = getBundleStartDateWithSanity(bundleId, baseSubscription, plan, startEffectiveDate, startEffectiveDate, context);
-                        final UUID subscriptionId = UUID.randomUUID();
+                        final UUID subscriptionId = UUIDs.randomUUID();
                         dryRunEvents = apiService.getEventsOnCreation(bundleId, subscriptionId, startEffectiveDate, bundleStartDate, 1L, plan, inputSpec.getPhaseType(), realPriceList,
                                                                       utcNow, startEffectiveDate, utcNow, false, context);
                         final SubscriptionBuilder builder = new SubscriptionBuilder()
diff --git a/subscription/src/main/java/org/killbill/billing/subscription/api/transfer/DefaultSubscriptionBaseTransferApi.java b/subscription/src/main/java/org/killbill/billing/subscription/api/transfer/DefaultSubscriptionBaseTransferApi.java
index dab95a5..8a36c06 100644
--- a/subscription/src/main/java/org/killbill/billing/subscription/api/transfer/DefaultSubscriptionBaseTransferApi.java
+++ b/subscription/src/main/java/org/killbill/billing/subscription/api/transfer/DefaultSubscriptionBaseTransferApi.java
@@ -30,7 +30,6 @@ import org.killbill.billing.callcontext.InternalTenantContext;
 import org.killbill.billing.catalog.api.Catalog;
 import org.killbill.billing.catalog.api.CatalogApiException;
 import org.killbill.billing.catalog.api.CatalogService;
-import org.killbill.billing.catalog.api.Plan;
 import org.killbill.billing.catalog.api.PlanPhase;
 import org.killbill.billing.catalog.api.PlanPhaseSpecifier;
 import org.killbill.billing.catalog.api.ProductCategory;
@@ -58,6 +57,7 @@ import org.killbill.billing.subscription.events.user.ApiEventCancel;
 import org.killbill.billing.subscription.events.user.ApiEventChange;
 import org.killbill.billing.subscription.events.user.ApiEventTransfer;
 import org.killbill.billing.subscription.exceptions.SubscriptionBaseError;
+import org.killbill.billing.util.UUIDs;
 import org.killbill.billing.util.callcontext.CallContext;
 import org.killbill.billing.util.callcontext.InternalCallContextFactory;
 
@@ -262,7 +262,7 @@ public class DefaultSubscriptionBaseTransferApi extends SubscriptionApiBase impl
 
                 // Create the new subscription for the new bundle on the new account
                 final DefaultSubscriptionBase defaultSubscriptionBase = createSubscriptionForApiUse(new SubscriptionBuilder()
-                                                                                                            .setId(UUID.randomUUID())
+                                                                                                            .setId(UUIDs.randomUUID())
                                                                                                             .setBundleId(subscriptionBundleData.getId())
                                                                                                             .setCategory(productCategory)
                                                                                                             .setBundleStartDate(effectiveTransferDate)
diff --git a/subscription/src/main/java/org/killbill/billing/subscription/api/user/DefaultSubscriptionBaseBundle.java b/subscription/src/main/java/org/killbill/billing/subscription/api/user/DefaultSubscriptionBaseBundle.java
index dbef7be..df5ca19 100644
--- a/subscription/src/main/java/org/killbill/billing/subscription/api/user/DefaultSubscriptionBaseBundle.java
+++ b/subscription/src/main/java/org/killbill/billing/subscription/api/user/DefaultSubscriptionBaseBundle.java
@@ -20,8 +20,8 @@ import java.util.UUID;
 
 import org.joda.time.DateTime;
 
-import org.killbill.billing.entitlement.api.BlockingState;
 import org.killbill.billing.entity.EntityBase;
+import org.killbill.billing.util.UUIDs;
 
 public class DefaultSubscriptionBaseBundle extends EntityBase implements SubscriptionBaseBundle {
 
@@ -32,7 +32,7 @@ public class DefaultSubscriptionBaseBundle extends EntityBase implements Subscri
 
     public DefaultSubscriptionBaseBundle(final String name, final UUID accountId, final DateTime startDate, final DateTime originalCreatedDate,
                                          final DateTime createdDate, final DateTime updatedDate) {
-        this(UUID.randomUUID(), name, accountId, startDate, originalCreatedDate, createdDate, updatedDate);
+        this(UUIDs.randomUUID(), name, accountId, startDate, originalCreatedDate, createdDate, updatedDate);
     }
 
     public DefaultSubscriptionBaseBundle(final UUID id, final String key, final UUID accountId, final DateTime lastSysUpdate, final DateTime originalCreatedDate,
diff --git a/subscription/src/main/java/org/killbill/billing/subscription/engine/dao/DefaultSubscriptionDao.java b/subscription/src/main/java/org/killbill/billing/subscription/engine/dao/DefaultSubscriptionDao.java
index 075f841..fae7602 100644
--- a/subscription/src/main/java/org/killbill/billing/subscription/engine/dao/DefaultSubscriptionDao.java
+++ b/subscription/src/main/java/org/killbill/billing/subscription/engine/dao/DefaultSubscriptionDao.java
@@ -78,6 +78,7 @@ import org.killbill.billing.subscription.events.user.ApiEventChange;
 import org.killbill.billing.subscription.events.user.ApiEventMigrateBilling;
 import org.killbill.billing.subscription.events.user.ApiEventType;
 import org.killbill.billing.subscription.exceptions.SubscriptionBaseError;
+import org.killbill.billing.util.UUIDs;
 import org.killbill.billing.util.cache.CacheControllerDispatcher;
 import org.killbill.billing.util.dao.NonEntityDao;
 import org.killbill.billing.util.entity.Pagination;
@@ -695,7 +696,7 @@ public class DefaultSubscriptionDao extends EntityDaoBase<SubscriptionBundleMode
                     .setEventType(ApiEventType.MIGRATE_BILLING)
                     .setFromDisk(true)
                     .setTotalOrdering(migrateBillingEvent.getTotalOrdering())
-                    .setUuid(UUID.randomUUID())
+                    .setUuid(UUIDs.randomUUID())
                     .setSubscriptionId(migrateBillingEvent.getSubscriptionId())
                     .setCreatedDate(now)
                     .setUpdatedDate(now)
diff --git a/subscription/src/main/java/org/killbill/billing/subscription/events/EventBaseBuilder.java b/subscription/src/main/java/org/killbill/billing/subscription/events/EventBaseBuilder.java
index 280abe7..1c90547 100644
--- a/subscription/src/main/java/org/killbill/billing/subscription/events/EventBaseBuilder.java
+++ b/subscription/src/main/java/org/killbill/billing/subscription/events/EventBaseBuilder.java
@@ -19,6 +19,7 @@ package org.killbill.billing.subscription.events;
 import java.util.UUID;
 
 import org.joda.time.DateTime;
+import org.killbill.billing.util.UUIDs;
 
 @SuppressWarnings("unchecked")
 public class EventBaseBuilder<T extends EventBaseBuilder<T>> {
@@ -36,7 +37,7 @@ public class EventBaseBuilder<T extends EventBaseBuilder<T>> {
     private boolean isActive;
 
     public EventBaseBuilder() {
-        this.uuid = UUID.randomUUID();
+        this.uuid = UUIDs.randomUUID();
         this.isActive = true;
     }
 
diff --git a/subscription/src/test/java/org/killbill/billing/subscription/api/timeline/TestRepairWithError.java b/subscription/src/test/java/org/killbill/billing/subscription/api/timeline/TestRepairWithError.java
index d8baee8..5b81058 100644
--- a/subscription/src/test/java/org/killbill/billing/subscription/api/timeline/TestRepairWithError.java
+++ b/subscription/src/test/java/org/killbill/billing/subscription/api/timeline/TestRepairWithError.java
@@ -19,7 +19,6 @@ package org.killbill.billing.subscription.api.timeline;
 import java.util.Collections;
 import java.util.LinkedList;
 import java.util.List;
-import java.util.UUID;
 
 import org.joda.time.DateTime;
 import org.joda.time.Interval;
@@ -42,6 +41,7 @@ import org.killbill.billing.subscription.api.user.DefaultSubscriptionBase;
 import org.killbill.billing.subscription.api.user.SubscriptionBaseApiException;
 import org.killbill.billing.subscription.api.user.TestSubscriptionHelper.TestWithException;
 import org.killbill.billing.subscription.api.user.TestSubscriptionHelper.TestWithExceptionCallback;
+import org.killbill.billing.util.UUIDs;
 
 import static org.testng.Assert.assertEquals;
 
@@ -131,7 +131,7 @@ public class TestRepairWithError extends SubscriptionTestSuiteNoDB {
                 testUtil.sortEventsOnBundle(bundleRepair);
                 final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Assault-Rifle", ProductCategory.BASE, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, PhaseType.EVERGREEN);
                 final NewEvent ne = testUtil.createNewEvent(SubscriptionBaseTransitionType.CHANGE, baseSubscription.getStartDate().plusDays(10), spec);
-                final DeletedEvent de = testUtil.createDeletedEvent(UUID.randomUUID());
+                final DeletedEvent de = testUtil.createDeletedEvent(UUIDs.randomUUID());
                 final SubscriptionBaseTimeline sRepair = testUtil.createSubscriptionRepair(baseSubscription.getId(), Collections.singletonList(de), Collections.singletonList(ne));
 
                 final BundleBaseTimeline bRepair = testUtil.createBundleRepair(bundle.getId(), bundleRepair.getViewId(), Collections.singletonList(sRepair));
@@ -266,7 +266,7 @@ public class TestRepairWithError extends SubscriptionTestSuiteNoDB {
                 assertEquals(aoRepair.getExistingEvents().size(), 2);
 
                 final List<DeletedEvent> des = new LinkedList<SubscriptionBaseTimeline.DeletedEvent>();
-                //des.add(createDeletedEvent(aoRepair.getExistingEvents().get(1).getEventId()));        
+                //des.add(createDeletedEvent(aoRepair.getExistingEvents().get(1).getEventId()));
                 final DateTime aoCancelDate = aoSubscription.getStartDate().plusDays(10);
 
                 final NewEvent ne = testUtil.createNewEvent(SubscriptionBaseTransitionType.CANCEL, aoCancelDate, null);
@@ -334,10 +334,10 @@ public class TestRepairWithError extends SubscriptionTestSuiteNoDB {
 
 
                 DefaultSubscriptionBase aoSubscription = createSubscription("Laser-Scope", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME);
-                
+
                 BundleRepair bundleRepair = repairApi.getBundleRepair(bundle.getId());
                 sortEventsOnBundle(bundleRepair);
-                
+
                 DateTime newCreateTime = baseSubscription.getStartDate().plusDays(3);
 
                 PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Pistol", ProductCategory.BASE, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, PhaseType.TRIAL);
@@ -348,18 +348,18 @@ public class TestRepairWithError extends SubscriptionTestSuiteNoDB {
                 des.add(createDeletedEvent(bundleRepair.getSubscriptions().get(0).getExistingEvents().get(1).getEventId()));
 
                 SubscriptionRepair bpRepair = createSubscriptionReapir(baseSubscription.getId(), des, Collections.singletonList(ne));
-                
+
                 ne = createNewEvent(SubscriptionBaseTransitionType.CANCEL, clock.getUTCNow().minusDays(1),  null);
                 SubscriptionRepair aoRepair = createSubscriptionReapir(aoSubscription.getId(), Collections.<SubscriptionRepair.DeletedEvent>emptyList(), Collections.singletonList(ne));
-                
-                
+
+
                 List<SubscriptionRepair> allRepairs = new LinkedList<SubscriptionRepair>();
                 allRepairs.add(bpRepair);
                 allRepairs.add(aoRepair);
                 bundleRepair =  createBundleRepair(bundle.getId(), bundleRepair.getViewId(), allRepairs);
                 // FIRST ISSUE DRY RUN
                 BundleRepair bRepair =  createBundleRepair(bundle.getId(), bundleRepair.getViewId(), allRepairs);
-                
+
                 boolean dryRun = true;
                 repairApi.repairBundle(bRepair, dryRun, callcontext);
                 */
@@ -375,16 +375,16 @@ public class TestRepairWithError extends SubscriptionTestSuiteNoDB {
 
                 /*
                 // MOVE CLOCK -- JUST BEFORE END OF TRIAL
-                 *                 
+                 *
                 Interval it = new Interval(clock.getUTCNow(), clock.getUTCNow().plusDays(29));
                 clock.addDeltaFromReality(it.toDurationMillis());
 
                 clock.setDeltaFromReality(getDurationDay(29), 0);
-                
+
                 DefaultSubscriptionBase aoSubscription = createSubscription("Laser-Scope", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME);
-                
+
                 // MOVE CLOCK -- RIGHT OUT OF TRIAL
-                testListener.pushExpectedEvent(NextEvent.PHASE);                
+                testListener.pushExpectedEvent(NextEvent.PHASE);
                 clock.addDeltaFromReality(getDurationDay(5));
                 assertListenerStatus();
 
@@ -395,22 +395,22 @@ public class TestRepairWithError extends SubscriptionTestSuiteNoDB {
 
                 BundleRepair bundleRepair = repairApi.getBundleRepair(bundle.getId());
                 sortEventsOnBundle(bundleRepair);
-                
+
                 SubscriptionRepair bpRepair = getSubscriptionRepair(baseSubscription.getId(), bundleRepair);
                 SubscriptionRepair aoRepair = getSubscriptionRepair(aoSubscription.getId(), bundleRepair);
 
                 List<DeletedEvent> bpdes = new LinkedList<SubscriptionRepair.DeletedEvent>();
-                bpdes.add(createDeletedEvent(bpRepair.getExistingEvents().get(2).getEventId()));    
+                bpdes.add(createDeletedEvent(bpRepair.getExistingEvents().get(2).getEventId()));
                 bpRepair = createSubscriptionReapir(baseSubscription.getId(), bpdes, Collections.<NewEvent>emptyList());
-                
+
                 NewEvent ne = createNewEvent(SubscriptionBaseTransitionType.CANCEL, reapairTime, null);
                 aoRepair = createSubscriptionReapir(aoSubscription.getId(), Collections.<SubscriptionRepair.DeletedEvent>emptyList(), Collections.singletonList(ne));
-                
+
                 List<SubscriptionRepair> allRepairs = new LinkedList<SubscriptionRepair>();
                 allRepairs.add(bpRepair);
                 allRepairs.add(aoRepair);
                 bundleRepair =  createBundleRepair(bundle.getId(), bundleRepair.getViewId(), allRepairs);
-                
+
                 boolean dryRun = false;
                 repairApi.repairBundle(bundleRepair, dryRun, callcontext);
                 */
diff --git a/subscription/src/test/java/org/killbill/billing/subscription/DefaultSubscriptionTestInitializer.java b/subscription/src/test/java/org/killbill/billing/subscription/DefaultSubscriptionTestInitializer.java
index 41605f2..d723dad 100644
--- a/subscription/src/test/java/org/killbill/billing/subscription/DefaultSubscriptionTestInitializer.java
+++ b/subscription/src/test/java/org/killbill/billing/subscription/DefaultSubscriptionTestInitializer.java
@@ -36,6 +36,7 @@ import org.killbill.billing.subscription.api.SubscriptionBaseInternalApi;
 import org.killbill.billing.subscription.api.SubscriptionBaseService;
 import org.killbill.billing.subscription.api.user.SubscriptionBaseBundle;
 import org.killbill.billing.subscription.engine.core.DefaultSubscriptionBaseService;
+import org.killbill.billing.util.UUIDs;
 import org.killbill.clock.ClockMock;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -60,16 +61,16 @@ public class DefaultSubscriptionTestInitializer implements SubscriptionTestIniti
     }
 
     public AccountData initAccountData() {
-        final AccountData accountData = new MockAccountBuilder().name(UUID.randomUUID().toString().substring(1, 8))
+        final AccountData accountData = new MockAccountBuilder().name(UUIDs.randomUUID().toString().substring(1, 8))
                                                                 .firstNameLength(6)
-                                                                .email(UUID.randomUUID().toString().substring(1, 8))
-                                                                .phone(UUID.randomUUID().toString().substring(1, 8))
+                                                                .email(UUIDs.randomUUID().toString().substring(1, 8))
+                                                                .phone(UUIDs.randomUUID().toString().substring(1, 8))
                                                                 .migrated(false)
                                                                 .isNotifiedForInvoices(false)
-                                                                .externalKey(UUID.randomUUID().toString())
+                                                                .externalKey(UUIDs.randomUUID().toString())
                                                                 .billingCycleDayLocal(1)
                                                                 .currency(Currency.USD)
-                                                                .paymentMethodId(UUID.randomUUID())
+                                                                .paymentMethodId(UUIDs.randomUUID())
                                                                 .timeZone(DateTimeZone.forID("Europe/Paris"))
                                                                 .build();
 
diff --git a/subscription/src/test/java/org/killbill/billing/subscription/SubscriptionTestSuiteNoDB.java b/subscription/src/test/java/org/killbill/billing/subscription/SubscriptionTestSuiteNoDB.java
index 29f932f..9b74d30 100644
--- a/subscription/src/test/java/org/killbill/billing/subscription/SubscriptionTestSuiteNoDB.java
+++ b/subscription/src/test/java/org/killbill/billing/subscription/SubscriptionTestSuiteNoDB.java
@@ -55,6 +55,7 @@ import org.testng.annotations.BeforeMethod;
 import com.google.inject.Guice;
 import com.google.inject.Injector;
 import com.google.inject.Stage;
+import org.killbill.billing.util.UUIDs;
 
 public class SubscriptionTestSuiteNoDB extends GuicyKillbillTestSuiteNoDB {
 
@@ -128,7 +129,7 @@ public class SubscriptionTestSuiteNoDB extends GuicyKillbillTestSuiteNoDB {
 
         this.catalog = subscriptionTestInitializer.initCatalog(catalogService, internalCallContext);
         this.accountData = subscriptionTestInitializer.initAccountData();
-        final UUID accountId = UUID.randomUUID();
+        final UUID accountId = UUIDs.randomUUID();
         mockNonEntityDao.addTenantRecordIdMapping(accountId, internalCallContext);
         this.bundle = subscriptionTestInitializer.initBundle(accountId, subscriptionInternalApi, internalCallContext);
     }
diff --git a/tenant/src/main/java/org/killbill/billing/tenant/api/DefaultTenant.java b/tenant/src/main/java/org/killbill/billing/tenant/api/DefaultTenant.java
index 354546f..a7f56b6 100644
--- a/tenant/src/main/java/org/killbill/billing/tenant/api/DefaultTenant.java
+++ b/tenant/src/main/java/org/killbill/billing/tenant/api/DefaultTenant.java
@@ -24,6 +24,7 @@ import org.joda.time.DateTime;
 
 import org.killbill.billing.tenant.dao.TenantModelDao;
 import org.killbill.billing.entity.EntityBase;
+import org.killbill.billing.util.UUIDs;
 
 public class DefaultTenant extends EntityBase implements Tenant {
 
@@ -38,7 +39,7 @@ public class DefaultTenant extends EntityBase implements Tenant {
      * @param data TenantData new data for the tenant
      */
     public DefaultTenant(final TenantData data) {
-        this(UUID.randomUUID(), data);
+        this(UUIDs.randomUUID(), data);
     }
 
     /**
diff --git a/tenant/src/main/java/org/killbill/billing/tenant/dao/DefaultTenantDao.java b/tenant/src/main/java/org/killbill/billing/tenant/dao/DefaultTenantDao.java
index a401574..8fe8d82 100644
--- a/tenant/src/main/java/org/killbill/billing/tenant/dao/DefaultTenantDao.java
+++ b/tenant/src/main/java/org/killbill/billing/tenant/dao/DefaultTenantDao.java
@@ -38,6 +38,7 @@ import org.killbill.billing.util.cache.CacheControllerDispatcher;
 import org.killbill.billing.callcontext.InternalCallContext;
 import org.killbill.billing.callcontext.InternalTenantContext;
 import org.killbill.clock.Clock;
+import org.killbill.billing.util.UUIDs;
 import org.killbill.billing.util.dao.NonEntityDao;
 import org.killbill.billing.util.entity.dao.EntityDaoBase;
 import org.killbill.billing.util.entity.dao.EntitySqlDaoTransactionWrapper;
@@ -132,7 +133,7 @@ public class DefaultTenantDao extends EntityDaoBase<TenantModelDao, Tenant, Tena
         transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<Void>() {
             @Override
             public Void inTransaction(final EntitySqlDaoWrapperFactory entitySqlDaoWrapperFactory) throws Exception {
-                final TenantKVModelDao tenantKVModelDao = new TenantKVModelDao(UUID.randomUUID(), context.getCreatedDate(), context.getUpdatedDate(), key, value);
+                final TenantKVModelDao tenantKVModelDao = new TenantKVModelDao(UUIDs.randomUUID(), context.getCreatedDate(), context.getUpdatedDate(), key, value);
                 final TenantKVSqlDao tenantKVSqlDao = entitySqlDaoWrapperFactory.become(TenantKVSqlDao.class);
                 if (uniqueKey) {
                     deleteFromTransaction(key, entitySqlDaoWrapperFactory, context);
diff --git a/tenant/src/main/java/org/killbill/billing/tenant/dao/TenantBroadcastModelDao.java b/tenant/src/main/java/org/killbill/billing/tenant/dao/TenantBroadcastModelDao.java
index 081220f..9cbf4ef 100644
--- a/tenant/src/main/java/org/killbill/billing/tenant/dao/TenantBroadcastModelDao.java
+++ b/tenant/src/main/java/org/killbill/billing/tenant/dao/TenantBroadcastModelDao.java
@@ -20,6 +20,7 @@ package org.killbill.billing.tenant.dao;
 import java.util.UUID;
 
 import org.joda.time.DateTime;
+import org.killbill.billing.util.UUIDs;
 import org.killbill.billing.util.dao.TableName;
 import org.killbill.billing.util.entity.Entity;
 import org.killbill.billing.util.entity.dao.EntityModelDao;
@@ -35,7 +36,7 @@ public class TenantBroadcastModelDao extends EntityModelDaoBase implements Entit
     public TenantBroadcastModelDao() { /* For the DAO mapper */ }
 
     public TenantBroadcastModelDao(final Long targetRecordId, final String type, final UUID userToken) {
-        this(UUID.randomUUID(), null, null, type, userToken);
+        this(UUIDs.randomUUID(), null, null, type, userToken);
         this.targetRecordId = targetRecordId;
     }
 
diff --git a/usage/src/main/java/org/killbill/billing/usage/dao/RolledUpUsageModelDao.java b/usage/src/main/java/org/killbill/billing/usage/dao/RolledUpUsageModelDao.java
index b9a5a62..411765a 100644
--- a/usage/src/main/java/org/killbill/billing/usage/dao/RolledUpUsageModelDao.java
+++ b/usage/src/main/java/org/killbill/billing/usage/dao/RolledUpUsageModelDao.java
@@ -20,6 +20,7 @@ import java.util.UUID;
 
 import org.joda.time.DateTime;
 import org.joda.time.LocalDate;
+import org.killbill.billing.util.UUIDs;
 import org.killbill.billing.util.dao.TableName;
 import org.killbill.billing.util.entity.Entity;
 import org.killbill.billing.util.entity.dao.EntityModelDao;
@@ -43,7 +44,7 @@ public class RolledUpUsageModelDao extends EntityModelDaoBase implements EntityM
     }
 
     public RolledUpUsageModelDao(final UUID subscriptionId, final String unitType, final LocalDate recordDate, final Long amount) {
-        this(UUID.randomUUID(), null, null, subscriptionId, unitType, recordDate, amount);
+        this(UUIDs.randomUUID(), null, null, subscriptionId, unitType, recordDate, amount);
     }
 
     public UUID getSubscriptionId() {
diff --git a/util/src/main/java/org/killbill/billing/util/config/PaymentConfig.java b/util/src/main/java/org/killbill/billing/util/config/PaymentConfig.java
index 4c90b6e..e0fb8bf 100644
--- a/util/src/main/java/org/killbill/billing/util/config/PaymentConfig.java
+++ b/util/src/main/java/org/killbill/billing/util/config/PaymentConfig.java
@@ -61,15 +61,21 @@ public interface PaymentConfig extends KillbillConfig {
     @Description("Number of threads for plugin executor dispatcher")
     public int getPaymentPluginThreadNb();
 
-    @Config("org.killbill.payment.janitor.pending")
+    @Config("org.killbill.payment.janitor.attempts.delay")
     @Default("12h")
-    @Description("Delay after which pending transactions should be marked as failed")
-    public TimeSpan getJanitorPendingCleanupTime();
+    @Description("Delay before which unresolved attempt should be retried")
+    public TimeSpan getIncompleteAttemptsTimeSpanDelay();
 
-    @Config("org.killbill.payment.janitor.attempts")
-    @Default("15m")
-    @Description("Delay after which incomplete  attempts should be completed")
-    public TimeSpan getJanitorAttemptCompletionTime();
+    @Config("org.killbill.payment.janitor.transactions.delay")
+    @Default("3m")
+    @Description("Delay before which unresolved transactions should be retried")
+    public TimeSpan getIncompleteTransactionsTimeSpanDelay();
+
+
+    @Config("org.killbill.payment.janitor.transactions.giveup")
+    @Default("7d")
+    @Description("Delay after which unresolved transactions should be abandoned")
+    public TimeSpan getIncompleteTransactionsTimeSpanGiveup();
 
     @Config("org.killbill.payment.janitor.rate")
     @Default("1h")
diff --git a/util/src/main/java/org/killbill/billing/util/customfield/StringCustomField.java b/util/src/main/java/org/killbill/billing/util/customfield/StringCustomField.java
index 16292f7..77dd944 100644
--- a/util/src/main/java/org/killbill/billing/util/customfield/StringCustomField.java
+++ b/util/src/main/java/org/killbill/billing/util/customfield/StringCustomField.java
@@ -21,8 +21,9 @@ import java.util.UUID;
 import org.joda.time.DateTime;
 
 import org.killbill.billing.ObjectType;
-import org.killbill.billing.util.customfield.dao.CustomFieldModelDao;
 import org.killbill.billing.entity.EntityBase;
+import org.killbill.billing.util.UUIDs;
+import org.killbill.billing.util.customfield.dao.CustomFieldModelDao;
 
 public class StringCustomField extends EntityBase implements CustomField {
 
@@ -32,7 +33,7 @@ public class StringCustomField extends EntityBase implements CustomField {
     private final ObjectType objectType;
 
     public StringCustomField(final String name, final String value, final ObjectType objectType, final UUID objectId, final DateTime createdDate) {
-        this(UUID.randomUUID(), name, value, objectType, objectId, createdDate);
+        this(UUIDs.randomUUID(), name, value, objectType, objectId, createdDate);
     }
 
     public StringCustomField(final UUID id, final String name, final String value, final ObjectType objectType, final UUID objectId, final DateTime createdDate) {
diff --git a/util/src/main/java/org/killbill/billing/util/dao/EntityAudit.java b/util/src/main/java/org/killbill/billing/util/dao/EntityAudit.java
index e48775f..70f249f 100644
--- a/util/src/main/java/org/killbill/billing/util/dao/EntityAudit.java
+++ b/util/src/main/java/org/killbill/billing/util/dao/EntityAudit.java
@@ -20,12 +20,12 @@ import java.util.UUID;
 
 import org.joda.time.DateTime;
 
+import org.killbill.billing.util.UUIDs;
 import org.killbill.billing.util.audit.ChangeType;
-import org.killbill.billing.entity.EntityBase;
 import org.killbill.billing.util.entity.dao.EntityModelDaoBase;
 
 public class EntityAudit extends EntityModelDaoBase {
-    
+
     private final TableName tableName;
     private final Long targetRecordId;
     private final ChangeType changeType;
@@ -38,7 +38,7 @@ public class EntityAudit extends EntityModelDaoBase {
 
     }
     public EntityAudit(final TableName tableName, final Long targetRecordId, final ChangeType changeType, final DateTime createdDate) {
-        this(UUID.randomUUID(), tableName, targetRecordId, changeType, createdDate);
+        this(UUIDs.randomUUID(), tableName, targetRecordId, changeType, createdDate);
     }
 
     public TableName getTableName() {
diff --git a/util/src/main/java/org/killbill/billing/util/dao/EntityHistoryModelDao.java b/util/src/main/java/org/killbill/billing/util/dao/EntityHistoryModelDao.java
index 6269944..447f132 100644
--- a/util/src/main/java/org/killbill/billing/util/dao/EntityHistoryModelDao.java
+++ b/util/src/main/java/org/killbill/billing/util/dao/EntityHistoryModelDao.java
@@ -23,6 +23,7 @@ import org.joda.time.DateTime;
 import org.killbill.billing.util.audit.ChangeType;
 import org.killbill.billing.util.entity.Entity;
 import org.killbill.billing.entity.EntityBase;
+import org.killbill.billing.util.UUIDs;
 import org.killbill.billing.util.entity.dao.EntityModelDao;
 
 public class EntityHistoryModelDao<M extends EntityModelDao<E>, E extends Entity> extends EntityBase {
@@ -39,7 +40,7 @@ public class EntityHistoryModelDao<M extends EntityModelDao<E>, E extends Entity
     }
 
     public EntityHistoryModelDao(final M src, final Long targetRecordId, final ChangeType type, final DateTime createdDate) {
-        this(UUID.randomUUID(), src, targetRecordId, type, createdDate);
+        this(UUIDs.randomUUID(), src, targetRecordId, type, createdDate);
     }
 
     public ChangeType getChangeType() {
diff --git a/util/src/main/java/org/killbill/billing/util/security/shiro/dao/JDBCSessionDao.java b/util/src/main/java/org/killbill/billing/util/security/shiro/dao/JDBCSessionDao.java
index 9e6df64..61f4e6d 100644
--- a/util/src/main/java/org/killbill/billing/util/security/shiro/dao/JDBCSessionDao.java
+++ b/util/src/main/java/org/killbill/billing/util/security/shiro/dao/JDBCSessionDao.java
@@ -24,7 +24,11 @@ import java.io.Serializable;
 import javax.inject.Inject;
 
 import org.apache.shiro.session.Session;
+import org.apache.shiro.session.UnknownSessionException;
+import org.apache.shiro.session.mgt.SimpleSession;
 import org.apache.shiro.session.mgt.eis.CachingSessionDAO;
+import org.joda.time.DateTime;
+import org.joda.time.DateTimeZone;
 import org.skife.jdbi.v2.IDBI;
 import org.skife.jdbi.v2.Transaction;
 import org.skife.jdbi.v2.TransactionStatus;
@@ -44,7 +48,28 @@ public class JDBCSessionDao extends CachingSessionDAO {
 
     @Override
     protected void doUpdate(final Session session) {
-        jdbcSessionSqlDao.update(new SessionModelDao(session));
+        // The look-up should be cheap (most likely cached)
+        final Session previousSession = readSession(session.getId());
+
+        if (SessionUtils.sameSession(previousSession, session)) {
+            // Only the last access time attribute was updated.
+            // Avoid writing the state to disk for each request: we don't care so much about precision in the database,
+            // we just want to make sure the session doesn't timeout too early.
+            // Note also that in the case of a single node (or distributed cache), the timeout computation
+            // will be correct (because the cache value is correct).
+            // See https://github.com/killbill/killbill/issues/326
+            if (!SessionUtils.accessedRecently(previousSession, session)) {
+                final DateTime lastAccessTime = new DateTime(session.getLastAccessTime(), DateTimeZone.UTC);
+                final Long sessionId = Long.valueOf(session.getId().toString());
+                jdbcSessionSqlDao.updateLastAccessTime(lastAccessTime, sessionId);
+            } else if (session instanceof SimpleSession) {
+                // Hack to override the value in the cache so subsequent requests see the (stale) value on disk
+                ((SimpleSession) session).setLastAccessTime(previousSession.getLastAccessTime());
+            }
+        } else {
+            // Various fields were changed, update the full row
+            jdbcSessionSqlDao.update(new SessionModelDao(session));
+        }
     }
 
     @Override
@@ -67,6 +92,28 @@ public class JDBCSessionDao extends CachingSessionDAO {
     }
 
     @Override
+    public Session readSession(final Serializable sessionId) throws UnknownSessionException {
+        final Session session = super.readSession(sessionId);
+
+        // Clone the session to avoid making changes to the existing one in the cache.
+        // This is required for the lookup in doUpdate to work
+        final SimpleSession clonedSession = new SimpleSession();
+        clonedSession.setId(session.getId());
+        clonedSession.setStartTimestamp(session.getStartTimestamp());
+        clonedSession.setLastAccessTime(session.getLastAccessTime());
+        clonedSession.setTimeout(session.getTimeout());
+        clonedSession.setHost(session.getHost());
+        clonedSession.setAttributes(SessionUtils.getSessionAttributes(session));
+
+        if (session instanceof SimpleSession) {
+            clonedSession.setStopTimestamp(((SimpleSession) session).getStopTimestamp());
+            clonedSession.setExpired(((SimpleSession) session).isExpired());
+        }
+
+        return clonedSession;
+    }
+
+    @Override
     protected Session doReadSession(final Serializable sessionId) {
         // Shiro should not pass us a null sessionId, but be safe...
         if (sessionId == null) {
diff --git a/util/src/main/java/org/killbill/billing/util/security/shiro/dao/JDBCSessionSqlDao.java b/util/src/main/java/org/killbill/billing/util/security/shiro/dao/JDBCSessionSqlDao.java
index 676d082..ea99854 100644
--- a/util/src/main/java/org/killbill/billing/util/security/shiro/dao/JDBCSessionSqlDao.java
+++ b/util/src/main/java/org/killbill/billing/util/security/shiro/dao/JDBCSessionSqlDao.java
@@ -16,6 +16,7 @@
 
 package org.killbill.billing.util.security.shiro.dao;
 
+import org.joda.time.DateTime;
 import org.killbill.commons.jdbi.binder.SmartBindBean;
 import org.skife.jdbi.v2.sqlobject.Bind;
 import org.skife.jdbi.v2.sqlobject.SqlQuery;
@@ -36,6 +37,9 @@ public interface JDBCSessionSqlDao extends Transactional<JDBCSessionSqlDao> {
     public void update(@SmartBindBean final SessionModelDao sessionModelDao);
 
     @SqlUpdate
+    public void updateLastAccessTime(@Bind("lastAccessTime") final DateTime lastAccessTime, @Bind("recordId") final Long sessionId);
+
+    @SqlUpdate
     public void delete(@SmartBindBean final SessionModelDao sessionModelDao);
 
     @SqlQuery
diff --git a/util/src/main/java/org/killbill/billing/util/security/shiro/dao/SessionUtils.java b/util/src/main/java/org/killbill/billing/util/security/shiro/dao/SessionUtils.java
new file mode 100644
index 0000000..9885e6b
--- /dev/null
+++ b/util/src/main/java/org/killbill/billing/util/security/shiro/dao/SessionUtils.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2015 Groupon, Inc
+ * Copyright 2015 The Billing Project, LLC
+ *
+ * The Billing Project licenses this file to you under the Apache License, version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at:
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.killbill.billing.util.security.shiro.dao;
+
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+import javax.annotation.Nullable;
+
+import org.apache.shiro.session.Session;
+import org.apache.shiro.session.mgt.AbstractSessionManager;
+
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.MoreObjects;
+
+public abstract class SessionUtils {
+
+    public SessionUtils() {}
+
+    // Check if the session was recently accessed ("recently" means within 5% of the timeout length)
+    public static boolean accessedRecently(final Session previousSession, final Session session) {
+        final Long timeoutMillis = MoreObjects.firstNonNull(session.getTimeout(), AbstractSessionManager.DEFAULT_GLOBAL_SESSION_TIMEOUT);
+        final Long errorMarginInMillis = 5L * timeoutMillis / 100;
+        return accessedRecently(previousSession, session, errorMarginInMillis);
+    }
+
+    @VisibleForTesting
+    static boolean accessedRecently(final Session previousSession, final Session session, final Long errorMarginInMillis) {
+        return previousSession.getLastAccessTime() != null &&
+               session.getLastAccessTime() != null &&
+               session.getLastAccessTime().getTime() < previousSession.getLastAccessTime().getTime() + errorMarginInMillis;
+    }
+
+    public static boolean sameSession(final Session previousSession, final Session newSession) {
+        return (previousSession.getStartTimestamp() != null ? previousSession.getStartTimestamp().compareTo(newSession.getStartTimestamp()) == 0 : newSession.getStartTimestamp() == null) &&
+               (previousSession.getTimeout() == newSession.getTimeout()) &&
+               (previousSession.getHost() != null ? previousSession.getHost().equals(newSession.getHost()) : newSession.getHost() == null) &&
+               sameSessionAttributes(previousSession, newSession);
+    }
+
+    @VisibleForTesting
+    static boolean sameSessionAttributes(@Nullable final Session previousSession, @Nullable final Session newSession) {
+        final Map<Object, Object> previousSessionAttributes = getSessionAttributes(previousSession);
+        final Map<Object, Object> newSessionAttributes = getSessionAttributes(newSession);
+        return previousSessionAttributes != null ? previousSessionAttributes.equals(newSessionAttributes) : newSessionAttributes == null;
+    }
+
+    public static Map<Object, Object> getSessionAttributes(@Nullable final Session session) {
+        if (session == null || session.getAttributeKeys() == null) {
+            return null;
+        }
+
+        final Map<Object, Object> attributes = new LinkedHashMap<Object, Object>();
+        for (final Object attributeKey : session.getAttributeKeys()) {
+            attributes.put(attributeKey, session.getAttribute(attributeKey));
+        }
+        return attributes;
+    }
+}
diff --git a/util/src/main/java/org/killbill/billing/util/tag/dao/TagDefinitionModelDao.java b/util/src/main/java/org/killbill/billing/util/tag/dao/TagDefinitionModelDao.java
index dbde12c..337afbc 100644
--- a/util/src/main/java/org/killbill/billing/util/tag/dao/TagDefinitionModelDao.java
+++ b/util/src/main/java/org/killbill/billing/util/tag/dao/TagDefinitionModelDao.java
@@ -20,8 +20,8 @@ import java.util.UUID;
 
 import org.joda.time.DateTime;
 
+import org.killbill.billing.util.UUIDs;
 import org.killbill.billing.util.dao.TableName;
-import org.killbill.billing.entity.EntityBase;
 import org.killbill.billing.util.entity.dao.EntityModelDao;
 import org.killbill.billing.util.entity.dao.EntityModelDaoBase;
 import org.killbill.billing.util.tag.ControlTagType;
@@ -47,7 +47,7 @@ public class TagDefinitionModelDao extends EntityModelDaoBase implements EntityM
     }
 
     public TagDefinitionModelDao(final DateTime createdDate, final String name, final String description) {
-        this(UUID.randomUUID(), createdDate, createdDate, name, description);
+        this(UUIDs.randomUUID(), createdDate, createdDate, name, description);
     }
 
     public TagDefinitionModelDao(final TagDefinition tagDefinition) {
diff --git a/util/src/main/java/org/killbill/billing/util/tag/dao/TagModelDao.java b/util/src/main/java/org/killbill/billing/util/tag/dao/TagModelDao.java
index 2b80de8..5edf2d4 100644
--- a/util/src/main/java/org/killbill/billing/util/tag/dao/TagModelDao.java
+++ b/util/src/main/java/org/killbill/billing/util/tag/dao/TagModelDao.java
@@ -21,8 +21,8 @@ import java.util.UUID;
 import org.joda.time.DateTime;
 
 import org.killbill.billing.ObjectType;
+import org.killbill.billing.util.UUIDs;
 import org.killbill.billing.util.dao.TableName;
-import org.killbill.billing.entity.EntityBase;
 import org.killbill.billing.util.entity.dao.EntityModelDao;
 import org.killbill.billing.util.entity.dao.EntityModelDaoBase;
 import org.killbill.billing.util.tag.Tag;
@@ -38,7 +38,7 @@ public class TagModelDao extends EntityModelDaoBase implements EntityModelDao<Ta
 
     public TagModelDao(final DateTime createdDate, final UUID tagDefinitionId,
                        final UUID objectId, final ObjectType objectType) {
-        this(UUID.randomUUID(), createdDate, createdDate, tagDefinitionId, objectId, objectType);
+        this(UUIDs.randomUUID(), createdDate, createdDate, tagDefinitionId, objectId, objectType);
     }
 
     public TagModelDao(final UUID id, final DateTime createdDate, final DateTime updatedDate, final UUID tagDefinitionId,
diff --git a/util/src/main/java/org/killbill/billing/util/tag/DefaultControlTag.java b/util/src/main/java/org/killbill/billing/util/tag/DefaultControlTag.java
index 273d560..35c5f81 100644
--- a/util/src/main/java/org/killbill/billing/util/tag/DefaultControlTag.java
+++ b/util/src/main/java/org/killbill/billing/util/tag/DefaultControlTag.java
@@ -21,6 +21,7 @@ import java.util.UUID;
 import org.joda.time.DateTime;
 
 import org.killbill.billing.ObjectType;
+import org.killbill.billing.util.UUIDs;
 
 public class DefaultControlTag extends DescriptiveTag implements ControlTag {
 
@@ -28,7 +29,7 @@ public class DefaultControlTag extends DescriptiveTag implements ControlTag {
 
     // use to create new objects
     public DefaultControlTag(final ControlTagType controlTagType, final ObjectType objectType, final UUID objectId, final DateTime createdDate) {
-        this(UUID.randomUUID(), controlTagType, objectType, objectId, createdDate);
+        this(UUIDs.randomUUID(), controlTagType, objectType, objectId, createdDate);
     }
 
     // use to hydrate objects when loaded from the persistence layer
diff --git a/util/src/main/java/org/killbill/billing/util/tag/DefaultTagDefinition.java b/util/src/main/java/org/killbill/billing/util/tag/DefaultTagDefinition.java
index 1815fde..32d85f8 100644
--- a/util/src/main/java/org/killbill/billing/util/tag/DefaultTagDefinition.java
+++ b/util/src/main/java/org/killbill/billing/util/tag/DefaultTagDefinition.java
@@ -21,6 +21,7 @@ import java.util.UUID;
 
 import org.killbill.billing.ObjectType;
 import org.killbill.billing.entity.EntityBase;
+import org.killbill.billing.util.UUIDs;
 import org.killbill.billing.util.tag.dao.TagDefinitionModelDao;
 
 import com.fasterxml.jackson.annotation.JsonCreator;
@@ -39,7 +40,7 @@ public class DefaultTagDefinition extends EntityBase implements TagDefinition {
     }
 
     public DefaultTagDefinition(final String name, final String description, final Boolean isControlTag) {
-        this(UUID.randomUUID(), name, description, isControlTag);
+        this(UUIDs.randomUUID(), name, description, isControlTag);
     }
 
     public DefaultTagDefinition(final UUID id, final String name, final String description, final Boolean isControlTag) {
diff --git a/util/src/main/java/org/killbill/billing/util/tag/DescriptiveTag.java b/util/src/main/java/org/killbill/billing/util/tag/DescriptiveTag.java
index 7afb64b..49dd339 100644
--- a/util/src/main/java/org/killbill/billing/util/tag/DescriptiveTag.java
+++ b/util/src/main/java/org/killbill/billing/util/tag/DescriptiveTag.java
@@ -18,12 +18,11 @@ package org.killbill.billing.util.tag;
 
 import java.util.UUID;
 
-import javax.annotation.Nullable;
-
 import org.joda.time.DateTime;
 
 import org.killbill.billing.ObjectType;
 import org.killbill.billing.entity.EntityBase;
+import org.killbill.billing.util.UUIDs;
 
 public class DescriptiveTag extends EntityBase implements Tag {
 
@@ -41,7 +40,7 @@ public class DescriptiveTag extends EntityBase implements Tag {
 
     // use to create new objects
     public DescriptiveTag(final UUID tagDefinitionId, final ObjectType objectType, final UUID objectId, final DateTime createdDate) {
-        super(UUID.randomUUID(), createdDate, createdDate);
+        super(UUIDs.randomUUID(), createdDate, createdDate);
         this.tagDefinitionId = tagDefinitionId;
         this.objectType = objectType;
         this.objectId = objectId;
diff --git a/util/src/main/resources/org/killbill/billing/util/security/shiro/dao/JDBCSessionSqlDao.sql.stg b/util/src/main/resources/org/killbill/billing/util/security/shiro/dao/JDBCSessionSqlDao.sql.stg
index fe228d2..2599c29 100644
--- a/util/src/main/resources/org/killbill/billing/util/security/shiro/dao/JDBCSessionSqlDao.sql.stg
+++ b/util/src/main/resources/org/killbill/billing/util/security/shiro/dao/JDBCSessionSqlDao.sql.stg
@@ -40,6 +40,13 @@ where record_id = :recordId
 ;
 >>
 
+updateLastAccessTime() ::= <<
+update sessions set
+  last_access_time = :lastAccessTime
+where record_id = :recordId
+;
+>>
+
 delete() ::= <<
 delete from sessions
 where record_id = :recordId
diff --git a/util/src/test/java/org/killbill/billing/util/security/shiro/dao/TestJDBCSessionDao.java b/util/src/test/java/org/killbill/billing/util/security/shiro/dao/TestJDBCSessionDao.java
index c8da341..b943b2d 100644
--- a/util/src/test/java/org/killbill/billing/util/security/shiro/dao/TestJDBCSessionDao.java
+++ b/util/src/test/java/org/killbill/billing/util/security/shiro/dao/TestJDBCSessionDao.java
@@ -22,6 +22,7 @@ import java.util.UUID;
 
 import org.apache.shiro.session.Session;
 import org.apache.shiro.session.mgt.SimpleSession;
+import org.joda.time.DateTime;
 import org.killbill.billing.util.UtilTestSuiteWithEmbeddedDB;
 import org.testng.Assert;
 import org.testng.annotations.Test;
@@ -55,12 +56,19 @@ public class TestJDBCSessionDao extends UtilTestSuiteWithEmbeddedDB {
         final Session retrievedSession = jdbcSessionDao.doReadSession(sessionId);
         Assert.assertEquals(retrievedSession, session);
 
-        // Update
-        final String newHost = UUID.randomUUID().toString();
-        Assert.assertNotEquals(retrievedSession.getHost(), newHost);
-        session.setHost(newHost);
+        // Update too soon, the database state won't be updated
+        Date lastAccessTime = new Date(retrievedSession.getLastAccessTime().getTime() + 1000);
+        Assert.assertNotEquals(retrievedSession.getLastAccessTime(), lastAccessTime);
+        session.setLastAccessTime(lastAccessTime);
         jdbcSessionDao.doUpdate(session);
-        Assert.assertEquals(jdbcSessionDao.doReadSession(sessionId).getHost(), newHost);
+        Assert.assertEquals(jdbcSessionDao.doReadSession(sessionId).getLastAccessTime().compareTo(retrievedSession.getLastAccessTime()), 0);
+
+        // Actual database update
+        lastAccessTime = new Date(retrievedSession.getLastAccessTime().getTime() + 100000);
+        Assert.assertNotEquals(retrievedSession.getLastAccessTime(), lastAccessTime);
+        session.setLastAccessTime(lastAccessTime);
+        jdbcSessionDao.doUpdate(session);
+        Assert.assertEquals(jdbcSessionDao.doReadSession(sessionId).getLastAccessTime().compareTo(lastAccessTime), 0);
 
         // Delete
         jdbcSessionDao.doDelete(session);
@@ -69,8 +77,9 @@ public class TestJDBCSessionDao extends UtilTestSuiteWithEmbeddedDB {
 
     private SimpleSession createSession() {
         final SimpleSession simpleSession = new SimpleSession();
-        simpleSession.setStartTimestamp(new Date(System.currentTimeMillis() - 5000));
-        simpleSession.setLastAccessTime(new Date(System.currentTimeMillis()));
+        // Truncate milliseconds for MySQL
+        simpleSession.setStartTimestamp(DateTime.now().withTimeAtStartOfDay().minusSeconds(5).toDate());
+        simpleSession.setLastAccessTime(DateTime.now().withTimeAtStartOfDay().toDate());
         simpleSession.setTimeout(493934L);
         simpleSession.setHost(UUID.randomUUID().toString());
         simpleSession.setAttribute(UUID.randomUUID().toString(), Short.MIN_VALUE);
diff --git a/util/src/test/java/org/killbill/billing/util/security/shiro/dao/TestSessionUtils.java b/util/src/test/java/org/killbill/billing/util/security/shiro/dao/TestSessionUtils.java
new file mode 100644
index 0000000..387dfe6
--- /dev/null
+++ b/util/src/test/java/org/killbill/billing/util/security/shiro/dao/TestSessionUtils.java
@@ -0,0 +1,176 @@
+/*
+ * Copyright 2015 Groupon, Inc
+ * Copyright 2015 The Billing Project, LLC
+ *
+ * The Billing Project licenses this file to you under the Apache License, version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at:
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.killbill.billing.util.security.shiro.dao;
+
+import java.util.Date;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.UUID;
+
+import org.apache.shiro.session.mgt.SimpleSession;
+import org.killbill.billing.util.UtilTestSuiteNoDB;
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+import com.google.common.collect.ImmutableMap;
+
+public class TestSessionUtils extends UtilTestSuiteNoDB {
+
+    private static final long MINUTES_IN_MILLIS = 60 * 1000L;
+
+    @Test(groups = "fast")
+    public void testAccessedRecently() throws Exception {
+        final Long t2 = System.currentTimeMillis();
+        final Long t1 = t2 - (3 * MINUTES_IN_MILLIS);
+
+        final SimpleSession session1 = new SimpleSession();
+        final SimpleSession session2 = new SimpleSession();
+        session1.setLastAccessTime(null);
+        session2.setLastAccessTime(null);
+
+        Assert.assertFalse(SessionUtils.accessedRecently(session1, session2));
+
+        session1.setLastAccessTime(new Date(t1));
+        session2.setLastAccessTime(new Date(t2));
+
+        Assert.assertFalse(SessionUtils.accessedRecently(session1, session2));
+
+        // For a timeout of 1 hour, 5% is 3 minutes
+        session2.setTimeout(59 * MINUTES_IN_MILLIS);
+        Assert.assertFalse(SessionUtils.accessedRecently(session1, session2));
+
+        session2.setTimeout(60 * MINUTES_IN_MILLIS);
+        Assert.assertFalse(SessionUtils.accessedRecently(session1, session2));
+
+        session2.setTimeout(61 * MINUTES_IN_MILLIS);
+        Assert.assertTrue(SessionUtils.accessedRecently(session1, session2));
+    }
+
+    @Test(groups = "fast")
+    public void testAccessedRecentlyWithError() throws Exception {
+        final Long t2 = System.currentTimeMillis();
+        final Long t1 = t2 - (3 * MINUTES_IN_MILLIS);
+
+        final SimpleSession session1 = new SimpleSession();
+        final SimpleSession session2 = new SimpleSession();
+        session1.setLastAccessTime(null);
+        session2.setLastAccessTime(null);
+
+        Assert.assertFalse(SessionUtils.accessedRecently(session1, session2, 0L));
+        Assert.assertFalse(SessionUtils.accessedRecently(session1, session2, 3 * MINUTES_IN_MILLIS - 1));
+        Assert.assertFalse(SessionUtils.accessedRecently(session1, session2, 3 * MINUTES_IN_MILLIS));
+        Assert.assertFalse(SessionUtils.accessedRecently(session1, session2, 3 * MINUTES_IN_MILLIS + 1));
+
+        session1.setLastAccessTime(new Date(t1));
+
+        Assert.assertFalse(SessionUtils.accessedRecently(session1, session2, 0L));
+        Assert.assertFalse(SessionUtils.accessedRecently(session1, session2, 3 * MINUTES_IN_MILLIS - 1));
+        Assert.assertFalse(SessionUtils.accessedRecently(session1, session2, 3 * MINUTES_IN_MILLIS));
+        Assert.assertFalse(SessionUtils.accessedRecently(session1, session2, 3 * MINUTES_IN_MILLIS + 1));
+
+        session2.setLastAccessTime(new Date(t2));
+
+        Assert.assertFalse(SessionUtils.accessedRecently(session1, session2, 0L));
+        Assert.assertFalse(SessionUtils.accessedRecently(session1, session2, 3 * MINUTES_IN_MILLIS - 1));
+        Assert.assertFalse(SessionUtils.accessedRecently(session1, session2, 3 * MINUTES_IN_MILLIS));
+        Assert.assertTrue(SessionUtils.accessedRecently(session1, session2, 3 * MINUTES_IN_MILLIS + 1));
+    }
+
+    @Test(groups = "fast")
+    public void testSameSession() throws Exception {
+        final SimpleSession session1 = new SimpleSession();
+        final SimpleSession session2 = new SimpleSession();
+
+        Assert.assertTrue(SessionUtils.sameSession(session1, session2));
+        Assert.assertTrue(SessionUtils.sameSession(session2, session1));
+
+        session1.setStartTimestamp(new Date(2 * System.currentTimeMillis()));
+        Assert.assertFalse(SessionUtils.sameSession(session1, session2));
+        Assert.assertFalse(SessionUtils.sameSession(session2, session1));
+
+        session2.setStartTimestamp(session1.getStartTimestamp());
+        Assert.assertTrue(SessionUtils.sameSession(session1, session2));
+        Assert.assertTrue(SessionUtils.sameSession(session2, session1));
+
+        session1.setTimeout(12345L);
+        Assert.assertFalse(SessionUtils.sameSession(session1, session2));
+        Assert.assertFalse(SessionUtils.sameSession(session2, session1));
+
+        session2.setTimeout(session1.getTimeout());
+        Assert.assertTrue(SessionUtils.sameSession(session1, session2));
+        Assert.assertTrue(SessionUtils.sameSession(session2, session1));
+
+        session1.setHost(UUID.randomUUID().toString());
+        Assert.assertFalse(SessionUtils.sameSession(session1, session2));
+        Assert.assertFalse(SessionUtils.sameSession(session2, session1));
+
+        session2.setHost(session1.getHost());
+        Assert.assertTrue(SessionUtils.sameSession(session1, session2));
+        Assert.assertTrue(SessionUtils.sameSession(session2, session1));
+
+        session1.setAttributes(buildAttributes(UUID.randomUUID()));
+        Assert.assertFalse(SessionUtils.sameSession(session1, session2));
+        Assert.assertFalse(SessionUtils.sameSession(session2, session1));
+
+        session2.setAttributes(session1.getAttributes());
+        Assert.assertTrue(SessionUtils.sameSession(session1, session2));
+        Assert.assertTrue(SessionUtils.sameSession(session2, session1));
+    }
+
+    @Test(groups = "fast")
+    public void testSameSessionAttributes() throws Exception {
+        final UUID oneKey = UUID.randomUUID();
+        final SimpleSession session1 = new SimpleSession();
+        final SimpleSession session2 = new SimpleSession();
+        final SimpleSession session3 = new SimpleSession();
+        final Map<Object, Object> attributes = buildAttributes(oneKey);
+        session1.setAttributes(attributes);
+        session2.setAttributes(new LinkedHashMap<Object, Object>(attributes));
+
+        Assert.assertFalse(SessionUtils.sameSessionAttributes(session1, null));
+        Assert.assertFalse(SessionUtils.sameSessionAttributes(null, session1));
+        Assert.assertFalse(SessionUtils.sameSessionAttributes(session1, session3));
+
+        Assert.assertTrue(SessionUtils.sameSessionAttributes(null, null));
+        Assert.assertTrue(SessionUtils.sameSessionAttributes(session1, session1));
+        Assert.assertTrue(SessionUtils.sameSessionAttributes(session1, session2));
+        Assert.assertTrue(SessionUtils.sameSessionAttributes(session2, session1));
+
+        session2.removeAttribute(oneKey);
+
+        Assert.assertFalse(SessionUtils.sameSessionAttributes(session1, session2));
+        Assert.assertFalse(SessionUtils.sameSessionAttributes(session2, session1));
+    }
+
+    @Test(groups = "fast")
+    public void testGetSessionAttributes() throws Exception {
+        final SimpleSession session = new SimpleSession();
+        final Map<Object, Object> attributes = buildAttributes(UUID.randomUUID());
+        session.setAttributes(attributes);
+
+        Assert.assertEquals(SessionUtils.getSessionAttributes(session), attributes);
+    }
+
+    private Map<Object, Object> buildAttributes(final UUID oneKey) {
+        return ImmutableMap.<Object, Object>of(oneKey, 1L,
+                                               UUID.randomUUID(), "2",
+                                               UUID.randomUUID(), (short) 3,
+                                               UUID.randomUUID(), 4,
+                                               UUID.randomUUID(), UUID.randomUUID());
+    }
+}