killbill-memoizeit

Changes

Details

diff --git a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestCatalogRetireElements.java b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestCatalogRetireElements.java
new file mode 100644
index 0000000..ed65f98
--- /dev/null
+++ b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestCatalogRetireElements.java
@@ -0,0 +1,231 @@
+/*
+ * 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.util.List;
+
+import org.joda.time.LocalDate;
+import org.killbill.billing.account.api.Account;
+import org.killbill.billing.api.TestApiListener.NextEvent;
+import org.killbill.billing.catalog.api.BillingPeriod;
+import org.killbill.billing.catalog.api.PlanPhaseSpecifier;
+import org.killbill.billing.catalog.api.PriceListSet;
+import org.killbill.billing.catalog.api.ProductCategory;
+import org.killbill.billing.entitlement.api.DefaultEntitlement;
+import org.killbill.billing.entitlement.api.Entitlement;
+import org.killbill.billing.entitlement.api.EntitlementApiException;
+import org.killbill.billing.invoice.api.Invoice;
+import org.killbill.billing.payment.api.PluginProperty;
+import org.killbill.billing.platform.api.KillbillConfigSource;
+import org.testng.annotations.Test;
+
+import com.google.common.collect.ImmutableList;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNotNull;
+import static org.testng.Assert.assertTrue;
+import static org.testng.Assert.fail;
+
+public class TestCatalogRetireElements extends TestIntegrationBase {
+
+    @Override
+    protected KillbillConfigSource getConfigSource() {
+        return super.getConfigSource("/beatrixVersionedCatalog.properties");
+    }
+
+    @Test(groups = "slow")
+    public void testRetirePlan() throws Exception {
+
+        // Catalog v1 starts in 2011-01-01
+        // Catalog v2 starts in 2015-12-01
+        final LocalDate today = new LocalDate(2015, 11, 5);
+
+        // Set clock to the initial start date - we implicitly assume here that the account timezone is UTC
+        clock.setDay(today);
+
+        final Account account = createAccountWithNonOsgiPaymentMethod(getAccountData(1));
+        //accountUserApi.createAccount(getAccountData(1), callContext);
+
+        final String productName = "Pistol";
+        final BillingPeriod term = BillingPeriod.MONTHLY;
+
+        final DefaultEntitlement bpEntitlement =
+                createBaseEntitlementAndCheckForCompletion(account.getId(), "externalKey", productName,
+                                                           ProductCategory.BASE, term, NextEvent.CREATE, NextEvent.INVOICE);
+
+        assertNotNull(bpEntitlement);
+        assertEquals(invoiceUserApi.getInvoicesByAccount(account.getId(), callContext).size(), 1);
+
+        assertEquals(bpEntitlement.getSubscriptionBase().getCurrentPlan().getRecurringBillingPeriod(), BillingPeriod.MONTHLY);
+
+        // Move out a month.
+        busHandler.pushExpectedEvents(NextEvent.PHASE, NextEvent.INVOICE, NextEvent.PAYMENT);
+        clock.addMonths(1);
+        assertListenerStatus();
+
+        // Catalog v2 should start now.
+
+        final PlanPhaseSpecifier spec = new PlanPhaseSpecifier(productName, ProductCategory.BASE, term, PriceListSet.DEFAULT_PRICELIST_NAME, null);
+        final LocalDate effectiveDate = new LocalDate(clock.getUTCNow());
+        try {
+            entitlementApi.createBaseEntitlement(account.getId(), spec, "externalKey2", null, effectiveDate, ImmutableList.<PluginProperty>of(), callContext);
+            fail(); // force to fail is there is not an exception
+        } catch (EntitlementApiException e) {
+            assertTrue(e.getLocalizedMessage().startsWith("Could not find a plan matching: (product: 'Pistol', billing period: 'MONTHLY'"));
+        }
+
+        // Move out a month and verify 'Pistol' plan continue working as expected.
+        busHandler.pushExpectedEvents(NextEvent.INVOICE, NextEvent.PAYMENT);
+        clock.addMonths(1);
+        assertListenerStatus();
+
+        final List<Invoice> invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), callContext);
+        assertEquals(invoices.size(), 3);
+        for (Invoice invoice : invoices) {
+            assertEquals(invoice.getInvoiceItems().get(0).getPlanName(), "pistol-monthly");
+        }
+
+    }
+
+    @Test(groups = "slow")
+    public void testRetireProduct() throws Exception {
+
+        // Catalog v1 starts in 2011-01-01
+        // Catalog v3 starts in 2016-01-01
+        final LocalDate today = new LocalDate(2015, 11, 5);
+
+        // Set clock to the initial start date - we implicitly assume here that the account timezone is UTC
+        clock.setDay(today);
+
+        final Account account = createAccountWithNonOsgiPaymentMethod(getAccountData(1));
+        //accountUserApi.createAccount(getAccountData(1), callContext);
+
+        final String productName = "Pistol";
+        final BillingPeriod term = BillingPeriod.MONTHLY;
+
+        final DefaultEntitlement bpEntitlement =
+                createBaseEntitlementAndCheckForCompletion(account.getId(), "externalKey", productName,
+                                                           ProductCategory.BASE, term, NextEvent.CREATE, NextEvent.INVOICE);
+
+        assertNotNull(bpEntitlement);
+        assertEquals(invoiceUserApi.getInvoicesByAccount(account.getId(), callContext).size(), 1);
+
+        assertEquals(bpEntitlement.getSubscriptionBase().getCurrentPlan().getRecurringBillingPeriod(), BillingPeriod.MONTHLY);
+
+        // Move out a month.
+        busHandler.pushExpectedEvents(NextEvent.PHASE, NextEvent.INVOICE, NextEvent.PAYMENT);
+        clock.addMonths(1);
+        assertListenerStatus();
+
+        // Move out a month.
+        busHandler.pushExpectedEvents(NextEvent.INVOICE, NextEvent.PAYMENT);
+        clock.addMonths(1);
+        assertListenerStatus();
+
+        // Catalog v3 should start now.
+
+        final PlanPhaseSpecifier spec = new PlanPhaseSpecifier(productName, ProductCategory.BASE, term, PriceListSet.DEFAULT_PRICELIST_NAME, null);
+        final LocalDate effectiveDate = new LocalDate(clock.getUTCNow());
+        try {
+            entitlementApi.createBaseEntitlement(account.getId(), spec, "externalKey2", null, effectiveDate, ImmutableList.<PluginProperty>of(), callContext);
+            fail(); // force to fail is there is not an exception
+        } catch (EntitlementApiException e) {
+            assertTrue(e.getLocalizedMessage().startsWith("Could not find any product named 'Pistol'"));
+        }
+
+        // Move out a month and verify 'Pistol' plan continue working as expected.
+        busHandler.pushExpectedEvents(NextEvent.INVOICE, NextEvent.PAYMENT);
+        clock.addMonths(1);
+        assertListenerStatus();
+
+        final List<Invoice> invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), callContext);
+        assertEquals(invoices.size(), 4);
+        for (Invoice invoice : invoices) {
+            assertEquals(invoice.getInvoiceItems().get(0).getPlanName(), "pistol-monthly");
+        }
+
+    }
+
+    @Test(groups = "slow")
+    public void testRetirePriceList() throws Exception {
+
+        // Catalog v1 starts in 2011-01-01
+        // Catalog v2 starts in 2015-12-01
+        // Catalog v3 starts in 2016-01-01
+        final LocalDate today = new LocalDate(2015, 11, 1);
+
+        // Set clock to the initial start date - we implicitly assume here that the account timezone is UTC
+        clock.setDay(today);
+
+        final Account account = createAccountWithNonOsgiPaymentMethod(getAccountData(1));
+        //accountUserApi.createAccount(getAccountData(1), callContext);
+
+        final String productName = "Pistol";
+        final BillingPeriod term = BillingPeriod.MONTHLY;
+        final PlanPhaseSpecifier spec = new PlanPhaseSpecifier(productName, ProductCategory.BASE, term, "SpecialDiscount", null);
+        LocalDate effectiveDate = new LocalDate(clock.getUTCNow());
+
+        busHandler.pushExpectedEvents(NextEvent.CREATE, NextEvent.INVOICE);
+        final Entitlement bpEntitlement = entitlementApi.createBaseEntitlement(account.getId(), spec, "externalKey", null, effectiveDate, ImmutableList.<PluginProperty>of(), callContext);
+        assertListenerStatus();
+
+        assertNotNull(bpEntitlement);
+        assertEquals(invoiceUserApi.getInvoicesByAccount(account.getId(), callContext).size(), 1);
+
+        // Move out a month.
+        busHandler.pushExpectedEvents(NextEvent.PHASE, NextEvent.INVOICE, NextEvent.PAYMENT);
+        clock.addMonths(1);
+        assertListenerStatus();
+
+        // Move out a month.
+        busHandler.pushExpectedEvents(NextEvent.INVOICE, NextEvent.PAYMENT);
+        clock.addMonths(1);
+        assertListenerStatus();
+
+        // PriceList "SpecialDiscount" at this point.
+
+        effectiveDate = new LocalDate(clock.getUTCNow());
+        try {
+            entitlementApi.createBaseEntitlement(account.getId(), spec, "externalKey2", null, effectiveDate, ImmutableList.<PluginProperty>of(), callContext);
+            fail(); // force to fail is there is not an exception
+        } catch (EntitlementApiException e) {
+            assertTrue(e.getLocalizedMessage().startsWith("Could not find any product named 'Pistol'"));
+        }
+
+        // Move out a month and verify 'Pistol' plan continue working as expected.
+        busHandler.pushExpectedEvents(NextEvent.INVOICE, NextEvent.PAYMENT);
+        clock.addMonths(1);
+        assertListenerStatus();
+
+        // Move out a month.
+        busHandler.pushExpectedEvents(NextEvent.PHASE, NextEvent.INVOICE, NextEvent.PAYMENT);
+        clock.addMonths(1);
+        assertListenerStatus();
+
+        final List<Invoice> invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), callContext);
+        assertEquals(invoices.size(), 5);
+
+        assertTrue(invoices.get(0).getInvoiceItems().get(0).getPhaseName().equals("discount-pistol-monthly-trial"));
+        assertTrue(invoices.get(1).getInvoiceItems().get(0).getPhaseName().equals("discount-pistol-monthly-discount"));
+        assertTrue(invoices.get(2).getInvoiceItems().get(0).getPhaseName().equals("discount-pistol-monthly-discount"));
+        assertTrue(invoices.get(3).getInvoiceItems().get(0).getPhaseName().equals("discount-pistol-monthly-discount"));
+        assertTrue(invoices.get(4).getInvoiceItems().get(0).getPhaseName().equals("discount-pistol-monthly-evergreen"));
+
+    }
+
+}
diff --git a/beatrix/src/test/resources/beatrixVersionedCatalog.properties b/beatrix/src/test/resources/beatrixVersionedCatalog.properties
new file mode 100644
index 0000000..689f6e4
--- /dev/null
+++ b/beatrix/src/test/resources/beatrixVersionedCatalog.properties
@@ -0,0 +1,20 @@
+#
+# 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.
+#
+
+org.killbill.catalog.uri=file:beatrix/src/test/resources/catalog
+org.killbill.payment.retry.days=8,8,8,8,8,8,8,8
+org.killbill.osgi.bundle.install.dir=/var/tmp/beatrix-bundles
diff --git a/beatrix/src/test/resources/catalog/WeaponsHireSmall-v1.xml b/beatrix/src/test/resources/catalog/WeaponsHireSmall-v1.xml
new file mode 100644
index 0000000..11044e6
--- /dev/null
+++ b/beatrix/src/test/resources/catalog/WeaponsHireSmall-v1.xml
@@ -0,0 +1,345 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!--
+  ~ Copyright 2010-2013 Ning, Inc.
+  ~
+  ~ Ning 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.
+  -->
+
+<catalog xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:noNamespaceSchemaLocation="CatalogSchema.xsd ">
+
+    <effectiveDate>2011-01-01T00:00:00+00:00</effectiveDate>
+    <catalogName>WeaponsHireSmall</catalogName>
+
+    <recurringBillingMode>IN_ADVANCE</recurringBillingMode>
+
+    <currencies>
+        <currency>USD</currency>
+        <currency>EUR</currency>
+        <currency>GBP</currency>
+    </currencies>
+
+    <units>
+        <unit name="targets"/>
+        <unit name="misfires"/>
+        <unit name="shells"/>
+    </units>
+
+    <products>
+        <product name="Pistol">
+            <category>BASE</category>
+        </product>
+        <product name="Shotgun">
+            <category>BASE</category>
+            <limits>
+                <limit>
+                    <unit>shells</unit>
+                    <max>300</max>
+                </limit>
+            </limits>
+        </product>
+        <product name="Laser-Scope">
+            <category>ADD_ON</category>
+        </product>
+        <product name="Extra-Ammo">
+            <category>ADD_ON</category>
+        </product>
+    </products>
+
+    <rules>
+        <changePolicy>
+            <changePolicyCase>
+                <fromBillingPeriod>MONTHLY</fromBillingPeriod>
+                <toProduct>Shotgun</toProduct>
+                <toBillingPeriod>MONTHLY</toBillingPeriod>
+                <policy>END_OF_TERM</policy>
+            </changePolicyCase>
+            <changePolicyCase>
+                <phaseType>TRIAL</phaseType>
+                <policy>IMMEDIATE</policy>
+            </changePolicyCase>
+        </changePolicy>
+        <changeAlignment>
+            <changeAlignmentCase>
+                <alignment>START_OF_SUBSCRIPTION</alignment>
+            </changeAlignmentCase>
+        </changeAlignment>
+        <createAlignment>
+            <createAlignmentCase>
+                <product>Laser-Scope</product>
+                <alignment>START_OF_SUBSCRIPTION</alignment>
+            </createAlignmentCase>
+            <createAlignmentCase>
+                <alignment>START_OF_BUNDLE</alignment>
+            </createAlignmentCase>
+        </createAlignment>
+        <priceList>
+            <priceListCase>
+                <fromPriceList>SpecialDiscount</fromPriceList>
+                <toPriceList>DEFAULT</toPriceList>
+            </priceListCase>
+            <priceListCase>
+                <toPriceList>DEFAULT</toPriceList>
+            </priceListCase>
+        </priceList>
+    </rules>
+
+    <plans>
+        <plan name="pistol-monthly">
+            <effectiveDateForExistingSubscriptons>2011-03-14T00:00:00+00:00</effectiveDateForExistingSubscriptons>
+            <product>Pistol</product>
+            <initialPhases>
+                <phase type="TRIAL">
+                    <duration>
+                        <unit>DAYS</unit>
+                        <number>30</number>
+                    </duration>
+                    <fixed>
+                        <fixedPrice> <!-- empty price implies $0 -->
+                        </fixedPrice>
+                    </fixed>
+                </phase>
+            </initialPhases>
+            <finalPhase type="EVERGREEN">
+                <duration>
+                    <unit>UNLIMITED</unit>
+                </duration>
+                <recurring>
+                    <billingPeriod>MONTHLY</billingPeriod>
+                    <recurringPrice>
+                        <price>
+                            <currency>GBP</currency>
+                            <value>49.95</value>
+                        </price>
+                        <price>
+                            <currency>EUR</currency>
+                            <value>49.95</value>
+                        </price>
+                        <price>
+                            <currency>USD</currency>
+                            <value>49.95</value>
+                        </price>
+                    </recurringPrice>
+                </recurring>
+            </finalPhase>
+        </plan>
+        <plan name="discount-pistol-monthly">
+            <product>Pistol</product>
+            <initialPhases>
+                <phase type="TRIAL">
+                    <duration>
+                        <unit>DAYS</unit>
+                        <number>30</number>
+                    </duration>
+                    <fixed>
+                        <fixedPrice> <!-- empty price implies $0 -->
+                        </fixedPrice>
+                    </fixed>
+                </phase>
+                <phase type="DISCOUNT">
+                    <duration>
+                        <unit>MONTHS</unit>
+                        <number>3</number>
+                    </duration>
+                    <recurring>
+                        <billingPeriod>MONTHLY</billingPeriod>
+                        <recurringPrice>
+                            <price>
+                                <currency>GBP</currency>
+                                <value>29.95</value>
+                            </price>
+                            <price>
+                                <currency>EUR</currency>
+                                <value>29.95</value>
+                            </price>
+                            <price>
+                                <currency>USD</currency>
+                                <value>29.95</value>
+                            </price>
+                        </recurringPrice>
+                    </recurring>
+                </phase>
+            </initialPhases>
+            <finalPhase type="EVERGREEN">
+                <duration>
+                    <unit>UNLIMITED</unit>
+                </duration>
+                <recurring>
+                    <billingPeriod>MONTHLY</billingPeriod>
+                    <recurringPrice>
+                        <price>
+                            <currency>GBP</currency>
+                            <value>49.95</value>
+                        </price>
+                        <price>
+                            <currency>EUR</currency>
+                            <value>49.95</value>
+                        </price>
+                        <price>
+                            <currency>USD</currency>
+                            <value>49.95</value>
+                        </price>
+                    </recurringPrice>
+                </recurring>
+            </finalPhase>
+        </plan>
+        <plan name="shotgun-monthly">
+            <product>Shotgun</product>
+            <initialPhases>
+                <phase type="TRIAL">
+                    <duration>
+                        <unit>DAYS</unit>
+                        <number>30</number>
+                    </duration>
+                    <fixed>
+                        <fixedPrice> <!-- empty price implies $0 -->
+                        </fixedPrice>
+                    </fixed>
+                </phase>
+            </initialPhases>
+            <finalPhase type="EVERGREEN">
+                <duration>
+                    <unit>UNLIMITED</unit>
+                    <number>-1</number>
+                </duration>
+                <recurring>
+                    <billingPeriod>MONTHLY</billingPeriod>
+                    <recurringPrice>
+                        <price>
+                            <currency>USD</currency>
+                            <value>249.95</value>
+                        </price>
+                        <price>
+                            <currency>EUR</currency>
+                            <value>149.95</value>
+                        </price>
+                        <price>
+                            <currency>GBP</currency>
+                            <value>169.95</value>
+                        </price>
+                    </recurringPrice>
+                </recurring>
+            </finalPhase>
+        </plan>
+        <plan name="shotgun-annual">
+            <product>Shotgun</product>
+            <initialPhases>
+                <phase type="TRIAL">
+                    <duration>
+                        <unit>DAYS</unit>
+                        <number>30</number>
+                    </duration>
+                    <fixed>
+                        <fixedPrice> <!-- empty price implies $0 -->
+                        </fixedPrice>
+                    </fixed>
+                </phase>
+            </initialPhases>
+            <finalPhase type="EVERGREEN">
+                <duration>
+                    <unit>UNLIMITED</unit>
+                </duration>
+                <recurring>
+                    <billingPeriod>ANNUAL</billingPeriod>
+                    <recurringPrice>
+                        <price>
+                            <currency>USD</currency>
+                            <value>2399.95</value>
+                        </price>
+                        <price>
+                            <currency>EUR</currency>
+                            <value>1499.95</value>
+                        </price>
+                        <price>
+                            <currency>GBP</currency>
+                            <value>1699.95</value>
+                        </price>
+                    </recurringPrice>
+                    <!--
+                    <limits>
+                        <limit>
+                            <unit>shells</unit>
+                            <max>200</max>
+                        </limit>
+                    </limits>
+                    -->
+                </recurring>
+            </finalPhase>
+        </plan>
+        <plan name="laser-scope-monthly">
+            <product>Laser-Scope</product>
+            <finalPhase type="EVERGREEN">
+                <duration>
+                    <unit>UNLIMITED</unit>
+                </duration>
+                <recurring>
+
+                    <billingPeriod>MONTHLY</billingPeriod>
+                    <recurringPrice>
+                        <price>
+                            <currency>USD</currency>
+                            <value>1999.95</value>
+                        </price>
+                        <price>
+                            <currency>EUR</currency>
+                            <value>1499.95</value>
+                        </price>
+                        <price>
+                            <currency>GBP</currency>
+                            <value>1999.95</value>
+                        </price>
+                    </recurringPrice>
+                </recurring>
+            </finalPhase>
+        </plan>
+        <plan name="extra-ammo-monthly">
+            <product>Extra-Ammo</product>
+            <finalPhase type="EVERGREEN">
+                <duration>
+                    <unit>UNLIMITED</unit>
+                </duration>
+                <recurring>
+                    <billingPeriod>MONTHLY</billingPeriod>
+                    <recurringPrice>
+                        <price>
+                            <currency>USD</currency>
+                            <value>1999.95</value>
+                        </price>
+                        <price>
+                            <currency>EUR</currency>
+                            <value>1499.95</value>
+                        </price>
+                        <price>
+                            <currency>GBP</currency>
+                            <value>1999.95</value>
+                        </price>
+                    </recurringPrice>
+                </recurring>
+            </finalPhase>
+        </plan>
+    </plans>
+    <priceLists>
+        <defaultPriceList name="DEFAULT">
+            <plans>
+                <plan>pistol-monthly</plan>
+                <plan>shotgun-monthly</plan>
+                <plan>shotgun-annual</plan>
+            </plans>
+        </defaultPriceList>
+        <childPriceList name="SpecialDiscount">
+            <plans>
+                <plan>discount-pistol-monthly</plan>
+            </plans>
+        </childPriceList>
+    </priceLists>
+</catalog>
diff --git a/beatrix/src/test/resources/catalog/WeaponsHireSmall-v2.xml b/beatrix/src/test/resources/catalog/WeaponsHireSmall-v2.xml
new file mode 100644
index 0000000..e1bed72
--- /dev/null
+++ b/beatrix/src/test/resources/catalog/WeaponsHireSmall-v2.xml
@@ -0,0 +1,306 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!--
+  ~ Copyright 2010-2013 Ning, Inc.
+  ~
+  ~ Ning 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.
+  -->
+
+<catalog xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:noNamespaceSchemaLocation="CatalogSchema.xsd ">
+
+    <effectiveDate>2015-12-01T00:00:00+00:00</effectiveDate>
+    <catalogName>WeaponsHireSmall</catalogName>
+
+    <recurringBillingMode>IN_ADVANCE</recurringBillingMode>
+
+    <currencies>
+        <currency>USD</currency>
+        <currency>EUR</currency>
+        <currency>GBP</currency>
+    </currencies>
+
+    <units>
+        <unit name="targets"/>
+        <unit name="misfires"/>
+        <unit name="shells"/>
+    </units>
+
+    <products>
+        <product name="Pistol">
+            <category>BASE</category>
+        </product>
+        <product name="Shotgun">
+            <category>BASE</category>
+            <limits>
+                <limit>
+                    <unit>shells</unit>
+                    <max>300</max>
+                </limit>
+            </limits>
+        </product>
+        <product name="Laser-Scope">
+            <category>ADD_ON</category>
+        </product>
+        <product name="Extra-Ammo">
+            <category>ADD_ON</category>
+        </product>
+    </products>
+
+    <rules>
+        <changePolicy>
+            <changePolicyCase>
+                <fromBillingPeriod>MONTHLY</fromBillingPeriod>
+                <toProduct>Shotgun</toProduct>
+                <toBillingPeriod>MONTHLY</toBillingPeriod>
+                <policy>END_OF_TERM</policy>
+            </changePolicyCase>
+            <changePolicyCase>
+                <phaseType>TRIAL</phaseType>
+                <policy>IMMEDIATE</policy>
+            </changePolicyCase>
+        </changePolicy>
+        <changeAlignment>
+            <changeAlignmentCase>
+                <alignment>START_OF_SUBSCRIPTION</alignment>
+            </changeAlignmentCase>
+        </changeAlignment>
+        <createAlignment>
+            <createAlignmentCase>
+                <product>Laser-Scope</product>
+                <alignment>START_OF_SUBSCRIPTION</alignment>
+            </createAlignmentCase>
+            <createAlignmentCase>
+                <alignment>START_OF_BUNDLE</alignment>
+            </createAlignmentCase>
+        </createAlignment>
+        <priceList>
+            <priceListCase>
+                <fromPriceList>SpecialDiscount</fromPriceList>
+                <toPriceList>DEFAULT</toPriceList>
+            </priceListCase>
+            <priceListCase>
+                <toPriceList>DEFAULT</toPriceList>
+            </priceListCase>
+        </priceList>
+    </rules>
+
+    <plans>
+        <plan name="discount-pistol-monthly">
+            <product>Pistol</product>
+            <initialPhases>
+                <phase type="TRIAL">
+                    <duration>
+                        <unit>DAYS</unit>
+                        <number>30</number>
+                    </duration>
+                    <fixed>
+                        <fixedPrice> <!-- empty price implies $0 -->
+                        </fixedPrice>
+                    </fixed>
+                </phase>
+                <phase type="DISCOUNT">
+                    <duration>
+                        <unit>MONTHS</unit>
+                        <number>3</number>
+                    </duration>
+                    <recurring>
+                        <billingPeriod>MONTHLY</billingPeriod>
+                        <recurringPrice>
+                            <price>
+                                <currency>GBP</currency>
+                                <value>29.95</value>
+                            </price>
+                            <price>
+                                <currency>EUR</currency>
+                                <value>29.95</value>
+                            </price>
+                            <price>
+                                <currency>USD</currency>
+                                <value>29.95</value>
+                            </price>
+                        </recurringPrice>
+                    </recurring>
+                </phase>
+            </initialPhases>
+            <finalPhase type="EVERGREEN">
+                <duration>
+                    <unit>UNLIMITED</unit>
+                </duration>
+                <recurring>
+                    <billingPeriod>MONTHLY</billingPeriod>
+                    <recurringPrice>
+                        <price>
+                            <currency>GBP</currency>
+                            <value>49.95</value>
+                        </price>
+                        <price>
+                            <currency>EUR</currency>
+                            <value>49.95</value>
+                        </price>
+                        <price>
+                            <currency>USD</currency>
+                            <value>49.95</value>
+                        </price>
+                    </recurringPrice>
+                </recurring>
+            </finalPhase>
+        </plan>
+        <plan name="shotgun-monthly">
+            <product>Shotgun</product>
+            <initialPhases>
+                <phase type="TRIAL">
+                    <duration>
+                        <unit>DAYS</unit>
+                        <number>30</number>
+                    </duration>
+                    <fixed>
+                        <fixedPrice> <!-- empty price implies $0 -->
+                        </fixedPrice>
+                    </fixed>
+                </phase>
+            </initialPhases>
+            <finalPhase type="EVERGREEN">
+                <duration>
+                    <unit>UNLIMITED</unit>
+                    <number>-1</number>
+                </duration>
+                <recurring>
+                    <billingPeriod>MONTHLY</billingPeriod>
+                    <recurringPrice>
+                        <price>
+                            <currency>USD</currency>
+                            <value>249.95</value>
+                        </price>
+                        <price>
+                            <currency>EUR</currency>
+                            <value>149.95</value>
+                        </price>
+                        <price>
+                            <currency>GBP</currency>
+                            <value>169.95</value>
+                        </price>
+                    </recurringPrice>
+                </recurring>
+            </finalPhase>
+        </plan>
+        <plan name="shotgun-annual">
+            <product>Shotgun</product>
+            <initialPhases>
+                <phase type="TRIAL">
+                    <duration>
+                        <unit>DAYS</unit>
+                        <number>30</number>
+                    </duration>
+                    <fixed>
+                        <fixedPrice> <!-- empty price implies $0 -->
+                        </fixedPrice>
+                    </fixed>
+                </phase>
+            </initialPhases>
+            <finalPhase type="EVERGREEN">
+                <duration>
+                    <unit>UNLIMITED</unit>
+                </duration>
+                <recurring>
+                    <billingPeriod>ANNUAL</billingPeriod>
+                    <recurringPrice>
+                        <price>
+                            <currency>USD</currency>
+                            <value>2399.95</value>
+                        </price>
+                        <price>
+                            <currency>EUR</currency>
+                            <value>1499.95</value>
+                        </price>
+                        <price>
+                            <currency>GBP</currency>
+                            <value>1699.95</value>
+                        </price>
+                    </recurringPrice>
+                    <!--
+                    <limits>
+                        <limit>
+                            <unit>shells</unit>
+                            <max>200</max>
+                        </limit>
+                    </limits>
+                    -->
+                </recurring>
+            </finalPhase>
+        </plan>
+        <plan name="laser-scope-monthly">
+            <product>Laser-Scope</product>
+            <finalPhase type="EVERGREEN">
+                <duration>
+                    <unit>UNLIMITED</unit>
+                </duration>
+                <recurring>
+
+                    <billingPeriod>MONTHLY</billingPeriod>
+                    <recurringPrice>
+                        <price>
+                            <currency>USD</currency>
+                            <value>1999.95</value>
+                        </price>
+                        <price>
+                            <currency>EUR</currency>
+                            <value>1499.95</value>
+                        </price>
+                        <price>
+                            <currency>GBP</currency>
+                            <value>1999.95</value>
+                        </price>
+                    </recurringPrice>
+                </recurring>
+            </finalPhase>
+        </plan>
+        <plan name="extra-ammo-monthly">
+            <product>Extra-Ammo</product>
+            <finalPhase type="EVERGREEN">
+                <duration>
+                    <unit>UNLIMITED</unit>
+                </duration>
+                <recurring>
+                    <billingPeriod>MONTHLY</billingPeriod>
+                    <recurringPrice>
+                        <price>
+                            <currency>USD</currency>
+                            <value>1999.95</value>
+                        </price>
+                        <price>
+                            <currency>EUR</currency>
+                            <value>1499.95</value>
+                        </price>
+                        <price>
+                            <currency>GBP</currency>
+                            <value>1999.95</value>
+                        </price>
+                    </recurringPrice>
+                </recurring>
+            </finalPhase>
+        </plan>
+    </plans>
+    <priceLists>
+        <defaultPriceList name="DEFAULT">
+            <plans>
+                <plan>shotgun-monthly</plan>
+                <plan>shotgun-annual</plan>
+            </plans>
+        </defaultPriceList>
+        <childPriceList name="SpecialDiscount">
+            <plans>
+                <plan>discount-pistol-monthly</plan>
+            </plans>
+        </childPriceList>
+    </priceLists>
+</catalog>
diff --git a/beatrix/src/test/resources/catalog/WeaponsHireSmall-v3.xml b/beatrix/src/test/resources/catalog/WeaponsHireSmall-v3.xml
new file mode 100644
index 0000000..f5501e5
--- /dev/null
+++ b/beatrix/src/test/resources/catalog/WeaponsHireSmall-v3.xml
@@ -0,0 +1,221 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!--
+  ~ 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.
+  -->
+
+<catalog xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:noNamespaceSchemaLocation="CatalogSchema.xsd ">
+
+    <effectiveDate>2016-01-01T00:00:00+00:00</effectiveDate>
+    <catalogName>WeaponsHireSmall</catalogName>
+
+    <recurringBillingMode>IN_ADVANCE</recurringBillingMode>
+
+    <currencies>
+        <currency>USD</currency>
+        <currency>EUR</currency>
+        <currency>GBP</currency>
+    </currencies>
+
+    <units>
+        <unit name="targets"/>
+        <unit name="misfires"/>
+        <unit name="shells"/>
+    </units>
+
+    <products>
+        <product name="Shotgun">
+            <category>BASE</category>
+            <limits>
+                <limit>
+                    <unit>shells</unit>
+                    <max>300</max>
+                </limit>
+            </limits>
+        </product>
+        <product name="Laser-Scope">
+            <category>ADD_ON</category>
+        </product>
+        <product name="Extra-Ammo">
+            <category>ADD_ON</category>
+        </product>
+    </products>
+
+    <rules>
+        <changePolicy>
+            <changePolicyCase>
+                <fromBillingPeriod>MONTHLY</fromBillingPeriod>
+                <toProduct>Shotgun</toProduct>
+                <toBillingPeriod>MONTHLY</toBillingPeriod>
+                <policy>END_OF_TERM</policy>
+            </changePolicyCase>
+            <changePolicyCase>
+                <phaseType>TRIAL</phaseType>
+                <policy>IMMEDIATE</policy>
+            </changePolicyCase>
+        </changePolicy>
+        <changeAlignment>
+            <changeAlignmentCase>
+                <alignment>START_OF_SUBSCRIPTION</alignment>
+            </changeAlignmentCase>
+        </changeAlignment>
+        <createAlignment>
+            <createAlignmentCase>
+                <product>Laser-Scope</product>
+                <alignment>START_OF_SUBSCRIPTION</alignment>
+            </createAlignmentCase>
+            <createAlignmentCase>
+                <alignment>START_OF_BUNDLE</alignment>
+            </createAlignmentCase>
+        </createAlignment>
+    </rules>
+
+    <plans>
+        <plan name="shotgun-monthly">
+            <product>Shotgun</product>
+            <initialPhases>
+                <phase type="TRIAL">
+                    <duration>
+                        <unit>DAYS</unit>
+                        <number>30</number>
+                    </duration>
+                    <fixed>
+                        <fixedPrice> <!-- empty price implies $0 -->
+                        </fixedPrice>
+                    </fixed>
+                </phase>
+            </initialPhases>
+            <finalPhase type="EVERGREEN">
+                <duration>
+                    <unit>UNLIMITED</unit>
+                    <number>-1</number>
+                </duration>
+                <recurring>
+                    <billingPeriod>MONTHLY</billingPeriod>
+                    <recurringPrice>
+                        <price>
+                            <currency>USD</currency>
+                            <value>249.95</value>
+                        </price>
+                        <price>
+                            <currency>EUR</currency>
+                            <value>149.95</value>
+                        </price>
+                        <price>
+                            <currency>GBP</currency>
+                            <value>169.95</value>
+                        </price>
+                    </recurringPrice>
+                </recurring>
+            </finalPhase>
+        </plan>
+        <plan name="shotgun-annual">
+            <product>Shotgun</product>
+            <initialPhases>
+                <phase type="TRIAL">
+                    <duration>
+                        <unit>DAYS</unit>
+                        <number>30</number>
+                    </duration>
+                    <fixed>
+                        <fixedPrice> <!-- empty price implies $0 -->
+                        </fixedPrice>
+                    </fixed>
+                </phase>
+            </initialPhases>
+            <finalPhase type="EVERGREEN">
+                <duration>
+                    <unit>UNLIMITED</unit>
+                </duration>
+                <recurring>
+                    <billingPeriod>ANNUAL</billingPeriod>
+                    <recurringPrice>
+                        <price>
+                            <currency>USD</currency>
+                            <value>2399.95</value>
+                        </price>
+                        <price>
+                            <currency>EUR</currency>
+                            <value>1499.95</value>
+                        </price>
+                        <price>
+                            <currency>GBP</currency>
+                            <value>1699.95</value>
+                        </price>
+                    </recurringPrice>
+                </recurring>
+            </finalPhase>
+        </plan>
+        <plan name="laser-scope-monthly">
+            <product>Laser-Scope</product>
+            <finalPhase type="EVERGREEN">
+                <duration>
+                    <unit>UNLIMITED</unit>
+                </duration>
+                <recurring>
+
+                    <billingPeriod>MONTHLY</billingPeriod>
+                    <recurringPrice>
+                        <price>
+                            <currency>USD</currency>
+                            <value>1999.95</value>
+                        </price>
+                        <price>
+                            <currency>EUR</currency>
+                            <value>1499.95</value>
+                        </price>
+                        <price>
+                            <currency>GBP</currency>
+                            <value>1999.95</value>
+                        </price>
+                    </recurringPrice>
+                </recurring>
+            </finalPhase>
+        </plan>
+        <plan name="extra-ammo-monthly">
+            <product>Extra-Ammo</product>
+            <finalPhase type="EVERGREEN">
+                <duration>
+                    <unit>UNLIMITED</unit>
+                </duration>
+                <recurring>
+                    <billingPeriod>MONTHLY</billingPeriod>
+                    <recurringPrice>
+                        <price>
+                            <currency>USD</currency>
+                            <value>1999.95</value>
+                        </price>
+                        <price>
+                            <currency>EUR</currency>
+                            <value>1499.95</value>
+                        </price>
+                        <price>
+                            <currency>GBP</currency>
+                            <value>1999.95</value>
+                        </price>
+                    </recurringPrice>
+                </recurring>
+            </finalPhase>
+        </plan>
+    </plans>
+    <priceLists>
+        <defaultPriceList name="DEFAULT">
+            <plans>
+                <plan>shotgun-monthly</plan>
+            </plans>
+        </defaultPriceList>
+    </priceLists>
+</catalog>
diff --git a/catalog/src/main/java/org/killbill/billing/catalog/DefaultPlan.java b/catalog/src/main/java/org/killbill/billing/catalog/DefaultPlan.java
index 0c1bc14..52e02e7 100644
--- a/catalog/src/main/java/org/killbill/billing/catalog/DefaultPlan.java
+++ b/catalog/src/main/java/org/killbill/billing/catalog/DefaultPlan.java
@@ -21,7 +21,6 @@ import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Date;
 import java.util.Iterator;
-import java.util.List;
 
 import javax.xml.bind.annotation.XmlAccessType;
 import javax.xml.bind.annotation.XmlAccessorType;
@@ -32,7 +31,6 @@ import javax.xml.bind.annotation.XmlID;
 import javax.xml.bind.annotation.XmlIDREF;
 
 import org.joda.time.DateTime;
-
 import org.killbill.billing.ErrorCode;
 import org.killbill.billing.catalog.api.BillingPeriod;
 import org.killbill.billing.catalog.api.CatalogApiException;
@@ -53,9 +51,6 @@ public class DefaultPlan extends ValidatingConfig<StandaloneCatalog> implements 
     @XmlID
     private String name;
 
-    @XmlAttribute(required = false)
-    private Boolean retired;
-
     //TODO MDW Validation - effectiveDateForExistingSubscriptons > catalog effectiveDate
     @XmlElement(required = false)
     private Date effectiveDateForExistingSubscriptons;
@@ -81,12 +76,10 @@ public class DefaultPlan extends ValidatingConfig<StandaloneCatalog> implements 
 
     public DefaultPlan() {
         initialPhases = new DefaultPlanPhase[0];
-        retired = false;
     }
 
     public DefaultPlan(final String planName, final DefaultPlan in, final PlanPhasePriceOverride[] overrides) {
         this.name = planName;
-        this.retired = in.isRetired();
         this.effectiveDateForExistingSubscriptons = in.getEffectiveDateForExistingSubscriptons();
         this.product = (DefaultProduct) in.getProduct();
         this.initialPhases = new DefaultPlanPhase[in.getInitialPhases().length];
@@ -129,11 +122,6 @@ public class DefaultPlan extends ValidatingConfig<StandaloneCatalog> implements 
     }
 
     @Override
-    public boolean isRetired() {
-        return retired;
-    }
-
-    @Override
     public DefaultPlanPhase getFinalPhase() {
         return finalPhase;
     }
@@ -243,11 +231,6 @@ public class DefaultPlan extends ValidatingConfig<StandaloneCatalog> implements 
         return this;
     }
 
-    public DefaultPlan setRetired(final boolean retired) {
-        this.retired = retired;
-        return this;
-    }
-
     public DefaultPlan setPlansAllowedInBundle(final Integer plansAllowedInBundle) {
         this.plansAllowedInBundle = plansAllowedInBundle;
         return this;
@@ -304,16 +287,12 @@ public class DefaultPlan extends ValidatingConfig<StandaloneCatalog> implements 
         if (product != null ? !product.equals(that.product) : that.product != null) {
             return false;
         }
-        if (retired != null ? !retired.equals(that.retired) : that.retired != null) {
-            return false;
-        }
         return true;
     }
 
     @Override
     public int hashCode() {
         int result = name != null ? name.hashCode() : 0;
-        result = 31 * result + (retired != null ? retired.hashCode() : 0);
         result = 31 * result + (effectiveDateForExistingSubscriptons != null ? effectiveDateForExistingSubscriptons.hashCode() : 0);
         result = 31 * result + (initialPhases != null ? Arrays.hashCode(initialPhases) : 0);
         result = 31 * result + (finalPhase != null ? finalPhase.hashCode() : 0);
@@ -323,7 +302,7 @@ public class DefaultPlan extends ValidatingConfig<StandaloneCatalog> implements 
 
     @Override
     public String toString() {
-        return "DefaultPlan [name=" + name + ", retired=" + retired + ", effectiveDateForExistingSubscriptons="
+        return "DefaultPlan [name=" + name + ", effectiveDateForExistingSubscriptons="
                 + effectiveDateForExistingSubscriptons + ", product=" + product + ", initialPhases="
                 + Arrays.toString(initialPhases) + ", finalPhase=" + finalPhase + ", plansAllowedInBundle="
                 + plansAllowedInBundle + "]";
diff --git a/catalog/src/main/java/org/killbill/billing/catalog/DefaultPriceList.java b/catalog/src/main/java/org/killbill/billing/catalog/DefaultPriceList.java
index c17d6ee..7d7903d 100644
--- a/catalog/src/main/java/org/killbill/billing/catalog/DefaultPriceList.java
+++ b/catalog/src/main/java/org/killbill/billing/catalog/DefaultPriceList.java
@@ -40,9 +40,6 @@ public class DefaultPriceList extends ValidatingConfig<StandaloneCatalog> implem
     @XmlID
     private String name;
 
-    @XmlAttribute(required = false)
-    private Boolean retired = false;
-
     @XmlElementWrapper(name = "plans", required = true)
     @XmlIDREF
     @XmlElement(name = "plan", required = true)
@@ -61,11 +58,6 @@ public class DefaultPriceList extends ValidatingConfig<StandaloneCatalog> implem
         return plans;
     }
 
-    @Override
-    public boolean isRetired() {
-        return retired;
-    }
-
     /* (non-Javadoc)
       * @see org.killbill.billing.catalog.IPriceList#getName()
       */
@@ -113,11 +105,6 @@ public class DefaultPriceList extends ValidatingConfig<StandaloneCatalog> implem
         return count;
     }
 
-    public DefaultPriceList setRetired(final boolean retired) {
-        this.retired = retired;
-        return this;
-    }
-
     public DefaultPriceList setName(final String name) {
         this.name = name;
         return this;
@@ -145,9 +132,6 @@ public class DefaultPriceList extends ValidatingConfig<StandaloneCatalog> implem
         if (!Arrays.equals(plans, that.plans)) {
             return false;
         }
-        if (retired != null ? !retired.equals(that.retired) : that.retired != null) {
-            return false;
-        }
 
         return true;
     }
@@ -155,7 +139,6 @@ public class DefaultPriceList extends ValidatingConfig<StandaloneCatalog> implem
     @Override
     public int hashCode() {
         int result = name != null ? name.hashCode() : 0;
-        result = 31 * result + (retired != null ? retired.hashCode() : 0);
         result = 31 * result + (plans != null ? Arrays.hashCode(plans) : 0);
         return result;
     }
diff --git a/catalog/src/main/java/org/killbill/billing/catalog/DefaultProduct.java b/catalog/src/main/java/org/killbill/billing/catalog/DefaultProduct.java
index e813ee1..9c0c1fd 100644
--- a/catalog/src/main/java/org/killbill/billing/catalog/DefaultProduct.java
+++ b/catalog/src/main/java/org/killbill/billing/catalog/DefaultProduct.java
@@ -16,6 +16,9 @@
 
 package org.killbill.billing.catalog;
 
+import java.net.URI;
+import java.util.Arrays;
+
 import javax.xml.bind.annotation.XmlAccessType;
 import javax.xml.bind.annotation.XmlAccessorType;
 import javax.xml.bind.annotation.XmlAttribute;
@@ -23,10 +26,7 @@ import javax.xml.bind.annotation.XmlElement;
 import javax.xml.bind.annotation.XmlElementWrapper;
 import javax.xml.bind.annotation.XmlID;
 import javax.xml.bind.annotation.XmlIDREF;
-import java.net.URI;
-import java.util.Arrays;
 
-import org.killbill.billing.catalog.api.BillingPeriod;
 import org.killbill.billing.catalog.api.Limit;
 import org.killbill.billing.catalog.api.Product;
 import org.killbill.billing.catalog.api.ProductCategory;
@@ -41,9 +41,6 @@ public class DefaultProduct extends ValidatingConfig<StandaloneCatalog> implemen
     @XmlID
     private String name;
 
-    @XmlAttribute(required = false)
-    private Boolean retired = false;
-
     @XmlElement(required = true)
     private ProductCategory category;
 
@@ -70,11 +67,6 @@ public class DefaultProduct extends ValidatingConfig<StandaloneCatalog> implemen
     }
 
     @Override
-    public boolean isRetired() {
-        return retired;
-    }
-
-    @Override
     public ProductCategory getCategory() {
         return category;
     }
@@ -191,14 +183,9 @@ public class DefaultProduct extends ValidatingConfig<StandaloneCatalog> implemen
         return this;
     }
 
-    public DefaultProduct setRetired(final boolean retired) {
-        this.retired = retired;
-        return this;
-    }
-
     @Override
     public String toString() {
-        return "DefaultProduct [name=" + name + ", retired=" + retired + ", category=" + category + ", included="
+        return "DefaultProduct [name=" + name + ", category=" + category + ", included="
                 + Arrays.toString(included) + ", available=" + Arrays.toString(available) + ", catalogName="
                 + catalogName + "]";
     }
@@ -232,9 +219,6 @@ public class DefaultProduct extends ValidatingConfig<StandaloneCatalog> implemen
         if (name != null ? !name.equals(that.name) : that.name != null) {
             return false;
         }
-        if (retired != null ? !retired.equals(that.retired) : that.retired != null) {
-            return false;
-        }
 
         return true;
     }
@@ -242,7 +226,6 @@ public class DefaultProduct extends ValidatingConfig<StandaloneCatalog> implemen
     @Override
     public int hashCode() {
         int result = name != null ? name.hashCode() : 0;
-        result = 31 * result + (retired != null ? retired.hashCode() : 0);
         result = 31 * result + (category != null ? category.hashCode() : 0);
         result = 31 * result + (included != null ? Arrays.hashCode(included) : 0);
         result = 31 * result + (available != null ? Arrays.hashCode(available) : 0);
diff --git a/catalog/src/main/java/org/killbill/billing/catalog/plugin/StandaloneCatalogMapper.java b/catalog/src/main/java/org/killbill/billing/catalog/plugin/StandaloneCatalogMapper.java
index af8617d..b8335b0 100644
--- a/catalog/src/main/java/org/killbill/billing/catalog/plugin/StandaloneCatalogMapper.java
+++ b/catalog/src/main/java/org/killbill/billing/catalog/plugin/StandaloneCatalogMapper.java
@@ -382,7 +382,6 @@ public class StandaloneCatalogMapper {
         final DefaultPriceList result = new DefaultPriceList();
         result.setName(input.getName());
         result.setPlans(toFilterDefaultPlans(ImmutableList.copyOf(input.getPlans())));
-        result.setRetired(input.isRetired());
         return result;
     }
 
@@ -393,7 +392,6 @@ public class StandaloneCatalogMapper {
         final PriceListDefault result = new PriceListDefault();
         result.setName(input.getName());
         result.setPlans(toFilterDefaultPlans(ImmutableList.copyOf(input.getPlans())));
-        result.setRetired(input.isRetired());
         return result;
     }
 
@@ -414,7 +412,6 @@ public class StandaloneCatalogMapper {
         result.setCatalogName(catalogName);
         result.setCatagory(input.getCategory());
         result.setName(input.getName());
-        result.setRetired(input.isRetired());
         return result;
     }
 
@@ -430,7 +427,6 @@ public class StandaloneCatalogMapper {
         }
         final DefaultPlan result = new DefaultPlan();
         result.setName(input.getName());
-        result.setRetired(input.isRetired());
         result.setEffectiveDateForExistingSubscriptons(input.getEffectiveDateForExistingSubscriptons());
         result.setFinalPhase(toDefaultPlanPhase(input.getFinalPhase()));
         result.setInitialPhases(toDefaultPlanPhases(ImmutableList.copyOf(input.getInitialPhases())));
diff --git a/catalog/src/main/java/org/killbill/billing/catalog/StandaloneCatalog.java b/catalog/src/main/java/org/killbill/billing/catalog/StandaloneCatalog.java
index 93dc2c7..0d02237 100644
--- a/catalog/src/main/java/org/killbill/billing/catalog/StandaloneCatalog.java
+++ b/catalog/src/main/java/org/killbill/billing/catalog/StandaloneCatalog.java
@@ -347,9 +347,9 @@ public class StandaloneCatalog extends ValidatingConfig<StandaloneCatalog> imple
         final Plan plan = createOrFindCurrentPlan(specifier.getProductName(), specifier.getBillingPeriod(), specifier.getPriceListName(), null);
         final DefaultPriceList priceList = findCurrentPriceList(specifier.getPriceListName());
 
-        return (!product.isRetired()) &&
-               (!plan.isRetired()) &&
-               (!priceList.isRetired());
+        return (product != null) &&
+               (plan != null) &&
+               (priceList != null);
     }
 
     @Override
diff --git a/catalog/src/test/java/org/killbill/billing/catalog/MockPriceList.java b/catalog/src/test/java/org/killbill/billing/catalog/MockPriceList.java
index 9937687..aa5a993 100644
--- a/catalog/src/test/java/org/killbill/billing/catalog/MockPriceList.java
+++ b/catalog/src/test/java/org/killbill/billing/catalog/MockPriceList.java
@@ -22,7 +22,6 @@ public class MockPriceList extends DefaultPriceList {
 
     public MockPriceList() {
         setName(PriceListSet.DEFAULT_PRICELIST_NAME);
-        setRetired(false);
         setPlans(MockPlan.createAll());
     }
 }
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 39117a5..595b3a9 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
@@ -1,6 +1,6 @@
 /*
- * Copyright 2014 Groupon, Inc
- * Copyright 2014 The Billing Project, LLC
+ * 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
@@ -19,6 +19,8 @@ package org.killbill.billing.payment.api;
 
 import java.util.List;
 import java.util.UUID;
+import java.util.concurrent.Callable;
+import java.util.concurrent.TimeUnit;
 
 import javax.annotation.Nullable;
 import javax.inject.Inject;
@@ -29,71 +31,92 @@ import org.killbill.billing.control.plugin.api.HPPType;
 import org.killbill.billing.control.plugin.api.PaymentApiType;
 import org.killbill.billing.control.plugin.api.PaymentControlApiException;
 import org.killbill.billing.control.plugin.api.PriorPaymentControlResult;
+import org.killbill.billing.payment.core.PaymentExecutors;
 import org.killbill.billing.payment.core.PaymentGatewayProcessor;
 import org.killbill.billing.payment.core.sm.control.ControlPluginRunner;
+import org.killbill.billing.payment.dispatcher.PluginDispatcher;
+import org.killbill.billing.payment.dispatcher.PluginDispatcher.PluginDispatcherReturnType;
 import org.killbill.billing.payment.plugin.api.GatewayNotification;
 import org.killbill.billing.payment.plugin.api.HostedPaymentPageFormDescriptor;
 import org.killbill.billing.util.callcontext.CallContext;
 import org.killbill.billing.util.callcontext.InternalCallContextFactory;
 import org.killbill.billing.util.config.PaymentConfig;
 
+import com.google.common.base.Joiner;
+
+import static org.killbill.billing.payment.dispatcher.PaymentPluginDispatcher.dispatchWithExceptionHandling;
+
 public class DefaultPaymentGatewayApi extends DefaultApiBase implements PaymentGatewayApi {
 
+    private static final Joiner JOINER = Joiner.on(", ");
+
     private final PaymentGatewayProcessor paymentGatewayProcessor;
     private final ControlPluginRunner controlPluginRunner;
+    private final PluginDispatcher<HostedPaymentPageFormDescriptor> paymentPluginFormDispatcher;
+    private final PluginDispatcher<GatewayNotification> paymentPluginNotificationDispatcher;
     private final InternalCallContextFactory internalCallContextFactory;
 
     @Inject
     public DefaultPaymentGatewayApi(final PaymentConfig paymentConfig,
                                     final PaymentGatewayProcessor paymentGatewayProcessor,
                                     final ControlPluginRunner controlPluginRunner,
+                                    final PaymentExecutors executors,
                                     final InternalCallContextFactory internalCallContextFactory) {
         super(paymentConfig);
         this.paymentGatewayProcessor = paymentGatewayProcessor;
         this.controlPluginRunner = controlPluginRunner;
+        final long paymentPluginTimeoutSec = TimeUnit.SECONDS.convert(paymentConfig.getPaymentPluginTimeout().getPeriod(), paymentConfig.getPaymentPluginTimeout().getUnit());
+        this.paymentPluginFormDispatcher = new PluginDispatcher<HostedPaymentPageFormDescriptor>(paymentPluginTimeoutSec, executors);
+        this.paymentPluginNotificationDispatcher = new PluginDispatcher<GatewayNotification>(paymentPluginTimeoutSec, executors);
         this.internalCallContextFactory = internalCallContextFactory;
     }
 
     @Override
     public HostedPaymentPageFormDescriptor buildFormDescriptor(final Account account, final UUID paymentMethodId, final Iterable<PluginProperty> customFields, final Iterable<PluginProperty> properties, final CallContext callContext) throws PaymentApiException {
-        final UUID paymentMethodIdToUse = paymentMethodId != null ? paymentMethodId : account.getPaymentMethodId();
-
-        if (paymentMethodId == null) {
-            throw new PaymentApiException(ErrorCode.PAYMENT_INVALID_PARAMETER, paymentMethodId, "should not be null");
-        }
-
-        return paymentGatewayProcessor.buildFormDescriptor(account, paymentMethodIdToUse, customFields, properties, callContext, internalCallContextFactory.createInternalCallContext(account.getId(), callContext));
+        return buildFormDescriptor(true, account, paymentMethodId, customFields, properties, callContext);
     }
 
     @Override
     public HostedPaymentPageFormDescriptor buildFormDescriptorWithPaymentControl(final Account account, final UUID paymentMethodId, final Iterable<PluginProperty> customFields, final Iterable<PluginProperty> properties, final PaymentOptions paymentOptions, final CallContext callContext) throws PaymentApiException {
-
-        return executeWithPaymentControl(account, paymentMethodId, properties, paymentOptions, callContext, new WithPaymentControlCallback<HostedPaymentPageFormDescriptor>() {
+        return executeWithPaymentControl(account, paymentMethodId, properties, paymentOptions, callContext, paymentPluginFormDispatcher, new WithPaymentControlCallback<HostedPaymentPageFormDescriptor>() {
             @Override
-            public HostedPaymentPageFormDescriptor doPaymentGatewayApiOperation(final Iterable<PluginProperty> adjustedPluginProperties) throws PaymentApiException {
-                return buildFormDescriptor(account, paymentMethodId, customFields, adjustedPluginProperties, callContext);
+            public HostedPaymentPageFormDescriptor doPaymentGatewayApiOperation(final UUID adjustedPaymentMethodId, final Iterable<PluginProperty> adjustedPluginProperties) throws PaymentApiException {
+                return buildFormDescriptor(false, account, adjustedPaymentMethodId, customFields, adjustedPluginProperties, callContext);
             }
         });
     }
 
     @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);
+        return paymentGatewayProcessor.processNotification(true, notification, pluginName, properties, callContext);
     }
 
     @Override
     public GatewayNotification processNotificationWithPaymentControl(final String notification, final String pluginName, final Iterable<PluginProperty> properties, final PaymentOptions paymentOptions, final CallContext callContext) throws PaymentApiException {
-        return executeWithPaymentControl(null, null, properties, paymentOptions, callContext, new WithPaymentControlCallback<GatewayNotification>() {
+        return executeWithPaymentControl(null, null, properties, paymentOptions, callContext, paymentPluginNotificationDispatcher, new WithPaymentControlCallback<GatewayNotification>() {
             @Override
-            public GatewayNotification doPaymentGatewayApiOperation(final Iterable<PluginProperty> adjustedPluginProperties) throws PaymentApiException {
-                return processNotification(notification, pluginName, adjustedPluginProperties, callContext);
+            public GatewayNotification doPaymentGatewayApiOperation(final UUID adjustedPaymentMethodId, final Iterable<PluginProperty> adjustedPluginProperties) throws PaymentApiException {
+                if (adjustedPaymentMethodId == null) {
+                    return paymentGatewayProcessor.processNotification(false, notification, pluginName, properties, callContext);
+                } else {
+                    return paymentGatewayProcessor.processNotification(false, notification, adjustedPaymentMethodId, properties, callContext);
+                }
             }
         });
     }
 
+    private HostedPaymentPageFormDescriptor buildFormDescriptor(final boolean shouldDispatch, final Account account, @Nullable final UUID paymentMethodId, final Iterable<PluginProperty> customFields, final Iterable<PluginProperty> properties, final CallContext callContext) throws PaymentApiException {
+        final UUID paymentMethodIdToUse = paymentMethodId != null ? paymentMethodId : account.getPaymentMethodId();
+        if (paymentMethodIdToUse == null) {
+            throw new PaymentApiException(ErrorCode.PAYMENT_INVALID_PARAMETER, "paymentMethodId", "should not be null");
+        }
+
+        return paymentGatewayProcessor.buildFormDescriptor(shouldDispatch, account, paymentMethodIdToUse, customFields, properties, callContext, internalCallContextFactory.createInternalCallContext(account.getId(), callContext));
+    }
 
     private interface WithPaymentControlCallback<T> {
-        T doPaymentGatewayApiOperation(final Iterable<PluginProperty> adjustedPluginProperties) throws PaymentApiException;
+
+        T doPaymentGatewayApiOperation(final UUID adjustedPaymentMethodId, final Iterable<PluginProperty> adjustedPluginProperties) throws PaymentApiException;
     }
 
     private <T> T executeWithPaymentControl(@Nullable final Account account,
@@ -101,42 +124,49 @@ public class DefaultPaymentGatewayApi extends DefaultApiBase implements PaymentG
                                             final Iterable<PluginProperty> properties,
                                             final PaymentOptions paymentOptions,
                                             final CallContext callContext,
+                                            final PluginDispatcher<T> pluginDispatcher,
                                             final WithPaymentControlCallback<T> callback) throws PaymentApiException {
-
         final List<String> paymentControlPluginNames = toPaymentControlPluginNames(paymentOptions);
         if (paymentControlPluginNames.isEmpty()) {
-            return callback.doPaymentGatewayApiOperation(properties);
+            return callback.doPaymentGatewayApiOperation(paymentMethodId, properties);
         }
 
-        final PriorPaymentControlResult priorCallResult;
-        try {
-            priorCallResult = controlPluginRunner.executePluginPriorCalls(account,
-                                                                          paymentMethodId,
-                                                                          null, null, null, null,
-                                                                          PaymentApiType.HPP, null, HPPType.BUILD_FORM_DESCRIPTOR,
-                                                                          null, null, true, paymentControlPluginNames, properties, callContext);
-
-        } catch (final PaymentControlApiException e) {
-            throw new PaymentApiException(ErrorCode.PAYMENT_PLUGIN_EXCEPTION, e);
-        }
-
-        try {
-            final T result = callback.doPaymentGatewayApiOperation(priorCallResult.getAdjustedPluginProperties());
-            controlPluginRunner.executePluginOnSuccessCalls(account,
-                                                            paymentMethodId,
-                                                            null, null, null, null, null,
-                                                            PaymentApiType.HPP, null, HPPType.BUILD_FORM_DESCRIPTOR,
-                                                            null, null, null, null, true, paymentControlPluginNames, priorCallResult.getAdjustedPluginProperties(), callContext);
-            return result;
-        } catch (final PaymentApiException e) {
-            controlPluginRunner.executePluginOnFailureCalls(account,
-                                                            paymentMethodId,
-                                                            null, null, null, null,
-                                                            PaymentApiType.HPP, null, HPPType.BUILD_FORM_DESCRIPTOR,
-                                                            null, null, true, paymentControlPluginNames, priorCallResult.getAdjustedPluginProperties(), callContext);
-
-            throw e;
-
-        }
+        final List<String> controlPluginNames = paymentOptions.getPaymentControlPluginNames();
+        return dispatchWithExceptionHandling(account,
+                                             JOINER.join(controlPluginNames),
+                                             new Callable<PluginDispatcherReturnType<T>>() {
+                                                 @Override
+                                                 public PluginDispatcherReturnType<T> call() throws Exception {
+                                                     final PriorPaymentControlResult priorCallResult;
+                                                     try {
+                                                         priorCallResult = controlPluginRunner.executePluginPriorCalls(account,
+                                                                                                                       paymentMethodId,
+                                                                                                                       null, null, null, null,
+                                                                                                                       PaymentApiType.HPP, null, HPPType.BUILD_FORM_DESCRIPTOR,
+                                                                                                                       null, null, true, paymentControlPluginNames, properties, callContext);
+
+                                                     } catch (final PaymentControlApiException e) {
+                                                         throw new PaymentApiException(ErrorCode.PAYMENT_PLUGIN_EXCEPTION, e);
+                                                     }
+
+                                                     try {
+                                                         final T result = callback.doPaymentGatewayApiOperation(priorCallResult.getAdjustedPaymentMethodId(), priorCallResult.getAdjustedPluginProperties());
+                                                         controlPluginRunner.executePluginOnSuccessCalls(account,
+                                                                                                         paymentMethodId,
+                                                                                                         null, null, null, null, null,
+                                                                                                         PaymentApiType.HPP, null, HPPType.BUILD_FORM_DESCRIPTOR,
+                                                                                                         null, null, null, null, true, paymentControlPluginNames, priorCallResult.getAdjustedPluginProperties(), callContext);
+                                                         return PluginDispatcher.createPluginDispatcherReturnType(result);
+                                                     } catch (final PaymentApiException e) {
+                                                         controlPluginRunner.executePluginOnFailureCalls(account,
+                                                                                                         paymentMethodId,
+                                                                                                         null, null, null, null,
+                                                                                                         PaymentApiType.HPP, null, HPPType.BUILD_FORM_DESCRIPTOR,
+                                                                                                         null, null, true, paymentControlPluginNames, priorCallResult.getAdjustedPluginProperties(), callContext);
+                                                         throw e;
+                                                     }
+                                                 }
+                                             },
+                                             pluginDispatcher);
     }
 }
diff --git a/payment/src/main/java/org/killbill/billing/payment/core/PaymentGatewayProcessor.java b/payment/src/main/java/org/killbill/billing/payment/core/PaymentGatewayProcessor.java
index 896a7c9..3ca5b2e 100644
--- a/payment/src/main/java/org/killbill/billing/payment/core/PaymentGatewayProcessor.java
+++ b/payment/src/main/java/org/killbill/billing/payment/core/PaymentGatewayProcessor.java
@@ -24,6 +24,7 @@ import java.util.concurrent.TimeUnit;
 import javax.inject.Inject;
 
 import org.killbill.billing.ErrorCode;
+import org.killbill.billing.ObjectType;
 import org.killbill.billing.account.api.Account;
 import org.killbill.billing.account.api.AccountInternalApi;
 import org.killbill.billing.callcontext.InternalCallContext;
@@ -49,11 +50,8 @@ import org.killbill.commons.locker.GlobalLocker;
 
 import com.google.common.base.Objects;
 
-// We don't take any lock here because the call needs to be re-entrant
-// from the plugin: for example, the BitPay plugin will create the payment during the
-// processNotification call, while the PayU plugin will create it during buildFormDescriptor.
-// These calls are not necessarily idempotent though (the PayU plugin will create
-// a voucher in the gateway during the buildFormDescriptor call).
+import static org.killbill.billing.payment.dispatcher.PaymentPluginDispatcher.dispatchWithExceptionHandling;
+
 public class PaymentGatewayProcessor extends ProcessorBase {
 
     private final PluginDispatcher<HostedPaymentPageFormDescriptor> paymentPluginFormDispatcher;
@@ -76,43 +74,66 @@ public class PaymentGatewayProcessor extends ProcessorBase {
         this.paymentPluginNotificationDispatcher = new PluginDispatcher<GatewayNotification>(paymentPluginTimeoutSec, executors);
     }
 
-    public GatewayNotification processNotification(final String notification, final String pluginName, final Iterable<PluginProperty> properties, final CallContext callContext) throws PaymentApiException {
-        return dispatchWithExceptionHandling(null,
-                                             pluginName,
-                                             new Callable<PluginDispatcherReturnType<GatewayNotification>>() {
-                                                 @Override
-                                                 public PluginDispatcherReturnType<GatewayNotification> call() throws PaymentApiException {
-                                                     final PaymentPluginApi plugin = getPaymentPluginApi(pluginName);
+    public GatewayNotification processNotification(final boolean shouldDispatch, final String notification, final UUID paymentMethodId, final Iterable<PluginProperty> properties, final CallContext callContext) throws PaymentApiException {
+        final String pluginName = getPaymentProviderPluginName(paymentMethodId, internalCallContextFactory.createInternalCallContext(paymentMethodId, ObjectType.PAYMENT_METHOD, callContext));
+        return processNotification(shouldDispatch, notification, pluginName, properties, callContext);
+    }
+
+    public GatewayNotification processNotification(final boolean shouldDispatch, final String notification, final String pluginName, final Iterable<PluginProperty> properties, final CallContext callContext) throws PaymentApiException {
+        if (shouldDispatch) {
+            return dispatchWithExceptionHandling(null,
+                                                 pluginName,
+                                                 new Callable<PluginDispatcherReturnType<GatewayNotification>>() {
+                                                     @Override
+                                                     public PluginDispatcherReturnType<GatewayNotification> call() throws PaymentApiException {
+                                                         final PaymentPluginApi plugin = getPaymentPluginApi(pluginName);
 
-                                                     try {
-                                                         final GatewayNotification result = plugin.processNotification(notification, properties, callContext);
-                                                         return PluginDispatcher.createPluginDispatcherReturnType(result == null ? new DefaultNoOpGatewayNotification() : result);
-                                                     } catch (final PaymentPluginApiException e) {
-                                                         throw new PaymentApiException(ErrorCode.PAYMENT_PLUGIN_EXCEPTION, e.getErrorMessage());
+                                                         try {
+                                                             final GatewayNotification result = plugin.processNotification(notification, properties, callContext);
+                                                             return PluginDispatcher.createPluginDispatcherReturnType(result == null ? new DefaultNoOpGatewayNotification() : result);
+                                                         } catch (final PaymentPluginApiException e) {
+                                                             throw new PaymentApiException(ErrorCode.PAYMENT_PLUGIN_EXCEPTION, e.getErrorMessage());
+                                                         }
                                                      }
-                                                 }
-                                             }, paymentPluginNotificationDispatcher);
+                                                 }, paymentPluginNotificationDispatcher);
+        } else {
+            final PaymentPluginApi plugin = getPaymentPluginApi(pluginName);
+            try {
+                return plugin.processNotification(notification, properties, callContext);
+            } catch (final PaymentPluginApiException e) {
+                throw new PaymentApiException(ErrorCode.PAYMENT_PLUGIN_EXCEPTION, e.getErrorMessage());
+            }
+        }
     }
 
-    public HostedPaymentPageFormDescriptor buildFormDescriptor(final Account account, final UUID paymentMethodId, final Iterable<PluginProperty> customFields, final Iterable<PluginProperty> properties, final CallContext callContext, final InternalCallContext internalCallContext) throws PaymentApiException {
+    public HostedPaymentPageFormDescriptor buildFormDescriptor(final boolean shouldDispatch, final Account account, final UUID paymentMethodId, final Iterable<PluginProperty> customFields, final Iterable<PluginProperty> properties, final CallContext callContext, final InternalCallContext internalCallContext) throws PaymentApiException {
         final String pluginName = getPaymentProviderPluginName(paymentMethodId, internalCallContext);
 
-        return dispatchWithExceptionHandling(account,
-                                             pluginName,
-                                             new Callable<PluginDispatcherReturnType<HostedPaymentPageFormDescriptor>>() {
-                                                 @Override
-                                                 public PluginDispatcherReturnType<HostedPaymentPageFormDescriptor> call() throws PaymentApiException {
-                                                     final PaymentPluginApi plugin = getPaymentPluginApi(pluginName);
+        if (shouldDispatch) {
+            return dispatchWithExceptionHandling(account,
+                                                 pluginName,
+                                                 new Callable<PluginDispatcherReturnType<HostedPaymentPageFormDescriptor>>() {
+                                                     @Override
+                                                     public PluginDispatcherReturnType<HostedPaymentPageFormDescriptor> call() throws PaymentApiException {
+                                                         final PaymentPluginApi plugin = getPaymentPluginApi(pluginName);
 
-                                                     try {
-                                                         final HostedPaymentPageFormDescriptor result = plugin.buildFormDescriptor(account.getId(), customFields, properties, callContext);
-                                                         return PluginDispatcher.createPluginDispatcherReturnType(result == null ? new DefaultNoOpHostedPaymentPageFormDescriptor(account.getId()) : result);
-                                                     } catch (final RuntimeException e) {
-                                                         throw new PaymentApiException(e, ErrorCode.PAYMENT_INTERNAL_ERROR, Objects.firstNonNull(e.getMessage(), ""));
-                                                     } catch (final PaymentPluginApiException e) {
-                                                         throw new PaymentApiException(ErrorCode.PAYMENT_PLUGIN_EXCEPTION, e.getErrorMessage());
+                                                         try {
+                                                             final HostedPaymentPageFormDescriptor result = plugin.buildFormDescriptor(account.getId(), customFields, properties, callContext);
+                                                             return PluginDispatcher.createPluginDispatcherReturnType(result == null ? new DefaultNoOpHostedPaymentPageFormDescriptor(account.getId()) : result);
+                                                         } catch (final RuntimeException e) {
+                                                             throw new PaymentApiException(e, ErrorCode.PAYMENT_INTERNAL_ERROR, Objects.firstNonNull(e.getMessage(), ""));
+                                                         } catch (final PaymentPluginApiException e) {
+                                                             throw new PaymentApiException(ErrorCode.PAYMENT_PLUGIN_EXCEPTION, e.getErrorMessage());
+                                                         }
                                                      }
-                                                 }
-                                             }, paymentPluginFormDispatcher);
+                                                 }, paymentPluginFormDispatcher);
+        } else {
+            final PaymentPluginApi plugin = getPaymentPluginApi(pluginName);
+            try {
+                return plugin.buildFormDescriptor(account.getId(), customFields, properties, callContext);
+            } catch (final PaymentPluginApiException e) {
+                throw new PaymentApiException(ErrorCode.PAYMENT_PLUGIN_EXCEPTION, e.getErrorMessage());
+            }
+        }
     }
 }
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 66e289d..f09a73f 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.common.collect.ImmutableList;
 import com.google.common.collect.Iterables;
 import com.google.inject.Inject;
 
+import static org.killbill.billing.payment.dispatcher.PaymentPluginDispatcher.dispatchWithExceptionHandling;
 import static org.killbill.billing.util.entity.dao.DefaultPaginationHelper.getEntityPagination;
 import static org.killbill.billing.util.entity.dao.DefaultPaginationHelper.getEntityPaginationFromPlugins;
 
@@ -438,6 +439,10 @@ public class PaymentMethodProcessor extends ProcessorBase {
                         throw new PaymentApiException(ErrorCode.PAYMENT_NO_SUCH_PAYMENT_METHOD, paymentMethodId);
                     }
 
+                    if (!paymentMethodModel.getAccountId().equals(account.getId())) {
+                        throw new PaymentApiException(ErrorCode.PAYMENT_DIFFERENT_ACCOUNT_ID, paymentMethodId);
+                    }
+
                     try {
                         final PaymentPluginApi pluginApi = getPluginApi(paymentMethodId, context);
 
diff --git a/payment/src/main/java/org/killbill/billing/payment/core/ProcessorBase.java b/payment/src/main/java/org/killbill/billing/payment/core/ProcessorBase.java
index 78f1d61..dd41668 100644
--- a/payment/src/main/java/org/killbill/billing/payment/core/ProcessorBase.java
+++ b/payment/src/main/java/org/killbill/billing/payment/core/ProcessorBase.java
@@ -22,8 +22,6 @@ import java.util.List;
 import java.util.Set;
 import java.util.UUID;
 import java.util.concurrent.Callable;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.TimeoutException;
 
 import javax.annotation.Nullable;
 
@@ -38,7 +36,6 @@ import org.killbill.billing.osgi.api.OSGIServiceRegistration;
 import org.killbill.billing.payment.api.PaymentApiException;
 import org.killbill.billing.payment.dao.PaymentDao;
 import org.killbill.billing.payment.dao.PaymentMethodModelDao;
-import org.killbill.billing.payment.dispatcher.PluginDispatcher;
 import org.killbill.billing.payment.dispatcher.PluginDispatcher.PluginDispatcherReturnType;
 import org.killbill.billing.payment.plugin.api.PaymentPluginApi;
 import org.killbill.billing.tag.TagInternalApi;
@@ -54,12 +51,10 @@ import org.killbill.clock.Clock;
 import org.killbill.commons.locker.GlobalLock;
 import org.killbill.commons.locker.GlobalLocker;
 import org.killbill.commons.locker.LockFailedException;
-import org.killbill.commons.request.Request;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import com.google.common.base.Function;
-import com.google.common.base.Objects;
 import com.google.common.collect.Collections2;
 
 public abstract class ProcessorBase {
@@ -209,36 +204,4 @@ public abstract class ProcessorBase {
             }
         }
     }
-
-    protected static <ReturnType> ReturnType dispatchWithExceptionHandling(@Nullable final Account account, final String pluginName, final Callable<PluginDispatcherReturnType<ReturnType>> callable, PluginDispatcher<ReturnType> pluginFormDispatcher) throws PaymentApiException {
-        final UUID accountId = account != null ? account.getId() : null;
-        final String accountExternalKey = account != null ? account.getExternalKey() : "";
-
-        try {
-            log.debug("Calling plugin {}", pluginName);
-            final ReturnType result = pluginFormDispatcher.dispatchWithTimeout(callable);
-            log.debug("Successful call of plugin {} for account {} with result {}", pluginName, accountExternalKey, result);
-            return result;
-        } catch (final TimeoutException e) {
-            final String errorMessage = String.format("TimeoutException during the execution of plugin %s", pluginName);
-            log.warn(errorMessage, e);
-            throw new PaymentApiException(ErrorCode.PAYMENT_PLUGIN_TIMEOUT, accountId, errorMessage);
-        } catch (final InterruptedException e) {
-            Thread.currentThread().interrupt();
-            final String errorMessage = String.format("InterruptedException during the execution of plugin %s", pluginName);
-            log.warn(errorMessage, e);
-            throw new PaymentApiException(ErrorCode.PAYMENT_INTERNAL_ERROR, Objects.firstNonNull(e.getMessage(), errorMessage));
-        } catch (final ExecutionException e) {
-            if (e.getCause() instanceof PaymentApiException) {
-                throw (PaymentApiException) e.getCause();
-            } else if (e.getCause() instanceof LockFailedException) {
-                final String format = String.format("Failed to lock account %s", accountExternalKey);
-                log.error(format, e);
-                throw new PaymentApiException(ErrorCode.PAYMENT_INTERNAL_ERROR, format);
-            } else {
-                throw new PaymentApiException(e, ErrorCode.PAYMENT_INTERNAL_ERROR, Objects.firstNonNull(e.getMessage(), ""));
-            }
-        }
-    }
-
 }
diff --git a/payment/src/main/java/org/killbill/billing/payment/core/sm/control/ControlPluginRunner.java b/payment/src/main/java/org/killbill/billing/payment/core/sm/control/ControlPluginRunner.java
index 2624d3e..7d4e20b 100644
--- a/payment/src/main/java/org/killbill/billing/payment/core/sm/control/ControlPluginRunner.java
+++ b/payment/src/main/java/org/killbill/billing/payment/core/sm/control/ControlPluginRunner.java
@@ -76,6 +76,9 @@ public class ControlPluginRunner {
         PriorPaymentControlResult prevResult = new DefaultPriorPaymentControlResult(false, amount, currency, paymentMethodId, pluginProperties);
 
         // Those values are adjusted prior each call with the result of what previous call to plugin returned
+        UUID inputPaymentMethodId = paymentMethodId;
+        BigDecimal inputAmount = amount;
+        Currency inputCurrency = currency;
         Iterable<PluginProperty> inputPluginProperties = pluginProperties;
         PaymentControlContext inputPaymentControlContext = new DefaultPaymentControlContext(account,
                                                                                             paymentMethodId,
@@ -101,6 +104,15 @@ public class ControlPluginRunner {
             log.debug("Calling priorCall of plugin {}", pluginName);
             prevResult = plugin.priorCall(inputPaymentControlContext, inputPluginProperties);
             log.debug("Successful executed priorCall of plugin {}", pluginName);
+            if (prevResult.getAdjustedPaymentMethodId() != null) {
+                inputPaymentMethodId = prevResult.getAdjustedPaymentMethodId();
+            }
+            if (prevResult.getAdjustedAmount() != null) {
+                inputAmount = prevResult.getAdjustedAmount();
+            }
+            if (prevResult.getAdjustedCurrency() != null) {
+                inputCurrency = prevResult.getAdjustedCurrency();
+            }
             if (prevResult.getAdjustedPluginProperties() != null) {
                 inputPluginProperties = prevResult.getAdjustedPluginProperties();
             }
@@ -108,7 +120,7 @@ public class ControlPluginRunner {
                 break;
             }
             inputPaymentControlContext = new DefaultPaymentControlContext(account,
-                                                                          prevResult.getAdjustedPaymentMethodId() != null ? prevResult.getAdjustedPaymentMethodId() : paymentMethodId,
+                                                                          inputPaymentMethodId,
                                                                           paymentAttemptId,
                                                                           paymentId,
                                                                           paymentExternalKey,
@@ -116,13 +128,13 @@ public class ControlPluginRunner {
                                                                           paymentApiType,
                                                                           transactionType,
                                                                           hppType,
-                                                                          prevResult.getAdjustedAmount() != null ? prevResult.getAdjustedAmount() : amount,
-                                                                          prevResult.getAdjustedCurrency() != null ? prevResult.getAdjustedCurrency() : currency,
+                                                                          inputAmount,
+                                                                          inputCurrency,
                                                                           isApiPayment,
                                                                           callContext);
         }
         // Rebuild latest result to include inputPluginProperties
-        prevResult = new DefaultPriorPaymentControlResult(prevResult, inputPluginProperties);
+        prevResult = new DefaultPriorPaymentControlResult(prevResult.isAborted(), inputPaymentMethodId, inputAmount, inputCurrency, inputPluginProperties);
         return prevResult;
     }
 
diff --git a/payment/src/main/java/org/killbill/billing/payment/dispatcher/PaymentPluginDispatcher.java b/payment/src/main/java/org/killbill/billing/payment/dispatcher/PaymentPluginDispatcher.java
new file mode 100644
index 0000000..8454892
--- /dev/null
+++ b/payment/src/main/java/org/killbill/billing/payment/dispatcher/PaymentPluginDispatcher.java
@@ -0,0 +1,71 @@
+/*
+ * 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.dispatcher;
+
+import java.util.UUID;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeoutException;
+
+import javax.annotation.Nullable;
+
+import org.killbill.billing.ErrorCode;
+import org.killbill.billing.account.api.Account;
+import org.killbill.billing.payment.api.PaymentApiException;
+import org.killbill.billing.payment.dispatcher.PluginDispatcher.PluginDispatcherReturnType;
+import org.killbill.commons.locker.LockFailedException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.base.MoreObjects;
+
+public class PaymentPluginDispatcher {
+
+    private static final Logger log = LoggerFactory.getLogger(PaymentPluginDispatcher.class);
+
+    public static <ReturnType> ReturnType dispatchWithExceptionHandling(@Nullable final Account account, final String pluginNames, final Callable<PluginDispatcherReturnType<ReturnType>> callable, final PluginDispatcher<ReturnType> pluginDispatcher) throws PaymentApiException {
+        final UUID accountId = account != null ? account.getId() : null;
+        final String accountExternalKey = account != null ? account.getExternalKey() : "";
+
+        try {
+            log.debug("Calling plugin(s) {}", pluginNames);
+            final ReturnType result = pluginDispatcher.dispatchWithTimeout(callable);
+            log.debug("Successful plugin(s) call of {} for account {} with result {}", pluginNames, accountId, result);
+            return result;
+        } catch (final TimeoutException e) {
+            final String errorMessage = String.format("TimeoutException while executing the plugin(s) %s", pluginNames);
+            log.warn(errorMessage, e);
+            throw new PaymentApiException(ErrorCode.PAYMENT_PLUGIN_TIMEOUT, accountId, errorMessage);
+        } catch (final InterruptedException e) {
+            Thread.currentThread().interrupt();
+            final String errorMessage = String.format("InterruptedException while executing the following plugin(s): %s", pluginNames);
+            log.warn(errorMessage, e);
+            throw new PaymentApiException(ErrorCode.PAYMENT_INTERNAL_ERROR, MoreObjects.firstNonNull(e.getMessage(), errorMessage));
+        } catch (final ExecutionException e) {
+            if (e.getCause() instanceof PaymentApiException) {
+                throw (PaymentApiException) e.getCause();
+            } else if (e.getCause() instanceof LockFailedException) {
+                final String format = String.format("Failed to lock account %s", accountExternalKey);
+                log.error(format, e);
+                throw new PaymentApiException(ErrorCode.PAYMENT_INTERNAL_ERROR, format);
+            } else {
+                throw new PaymentApiException(e, ErrorCode.PAYMENT_INTERNAL_ERROR, MoreObjects.firstNonNull(e.getMessage(), ""));
+            }
+        }
+    }
+}
diff --git a/payment/src/main/java/org/killbill/billing/payment/dispatcher/PluginDispatcher.java b/payment/src/main/java/org/killbill/billing/payment/dispatcher/PluginDispatcher.java
index e023d9f..a457e17 100644
--- a/payment/src/main/java/org/killbill/billing/payment/dispatcher/PluginDispatcher.java
+++ b/payment/src/main/java/org/killbill/billing/payment/dispatcher/PluginDispatcher.java
@@ -31,9 +31,11 @@ import org.killbill.commons.profiling.Profiling;
 import org.killbill.commons.profiling.ProfilingData;
 import org.killbill.commons.request.Request;
 
+import com.google.common.annotations.VisibleForTesting;
+
 public class PluginDispatcher<ReturnType> {
 
-    private final TimeUnit DEEFAULT_PLUGIN_TIMEOUT_UNIT = TimeUnit.SECONDS;
+    private final TimeUnit DEFAULT_PLUGIN_TIMEOUT_UNIT = TimeUnit.SECONDS;
 
     private final long timeoutSeconds;
     private final PaymentExecutors paymentExecutors;
@@ -45,10 +47,11 @@ public class PluginDispatcher<ReturnType> {
 
     // TODO Once we switch fully to automata, should this throw PaymentPluginApiException instead?
     public ReturnType dispatchWithTimeout(final Callable<PluginDispatcherReturnType<ReturnType>> task) throws TimeoutException, ExecutionException, InterruptedException {
-        return dispatchWithTimeout(task, timeoutSeconds, DEEFAULT_PLUGIN_TIMEOUT_UNIT);
+        return dispatchWithTimeout(task, timeoutSeconds, DEFAULT_PLUGIN_TIMEOUT_UNIT);
     }
 
-    public ReturnType dispatchWithTimeout(final Callable<PluginDispatcherReturnType<ReturnType>> task, final long timeout, final TimeUnit unit)
+    @VisibleForTesting
+    ReturnType dispatchWithTimeout(final Callable<PluginDispatcherReturnType<ReturnType>> task, final long timeout, final TimeUnit unit)
             throws TimeoutException, ExecutionException, InterruptedException {
 
         final ExecutorService pluginExecutor = paymentExecutors.getPluginExecutorService();
diff --git a/payment/src/main/java/org/killbill/billing/payment/retry/DefaultPriorPaymentControlResult.java b/payment/src/main/java/org/killbill/billing/payment/retry/DefaultPriorPaymentControlResult.java
index df030b0..27ce65b 100644
--- a/payment/src/main/java/org/killbill/billing/payment/retry/DefaultPriorPaymentControlResult.java
+++ b/payment/src/main/java/org/killbill/billing/payment/retry/DefaultPriorPaymentControlResult.java
@@ -53,8 +53,8 @@ public class DefaultPriorPaymentControlResult implements PriorPaymentControlResu
     }
 
 
-    public DefaultPriorPaymentControlResult(final PriorPaymentControlResult input, final Iterable<PluginProperty> adjustedPluginProperties) {
-        this(input.isAborted(), input.getAdjustedAmount(), input.getAdjustedCurrency(), input.getAdjustedPaymentMethodId(), adjustedPluginProperties);
+    public DefaultPriorPaymentControlResult(final boolean isAborted, final UUID adjustedPaymentMethodId, final BigDecimal adjustedAmount, final Currency adjustedCurrency, final Iterable<PluginProperty> adjustedPluginProperties) {
+        this(isAborted, adjustedAmount, adjustedCurrency, adjustedPaymentMethodId, adjustedPluginProperties);
     }
 
     @Override
diff --git a/payment/src/test/java/org/killbill/billing/payment/api/TestPaymentGatewayApiWithPaymentControl.java b/payment/src/test/java/org/killbill/billing/payment/api/TestPaymentGatewayApiWithPaymentControl.java
index 356e566..680221b 100644
--- a/payment/src/test/java/org/killbill/billing/payment/api/TestPaymentGatewayApiWithPaymentControl.java
+++ b/payment/src/test/java/org/killbill/billing/payment/api/TestPaymentGatewayApiWithPaymentControl.java
@@ -147,7 +147,8 @@ public class TestPaymentGatewayApiWithPaymentControl extends PaymentTestSuiteNoD
 
         validationPlugin.setExpectedProperties(expectedProperties);
 
-        paymentGatewayApi.buildFormDescriptorWithPaymentControl(account, account.getPaymentMethodId(), ImmutableList.<PluginProperty>of(), initialProperties, paymentOptions, callContext);
+        // Set a random UUID to verify the plugin will successfully override it
+        paymentGatewayApi.buildFormDescriptorWithPaymentControl(account, UUID.randomUUID(), ImmutableList.<PluginProperty>of(), initialProperties, paymentOptions, callContext);
 
     }
 
@@ -205,7 +206,7 @@ public class TestPaymentGatewayApiWithPaymentControl extends PaymentTestSuiteNoD
 
     }
 
-    public static class TestPaymentGatewayApiControlPlugin implements PaymentControlPluginApi {
+    public class TestPaymentGatewayApiControlPlugin implements PaymentControlPluginApi {
 
         public static final String PLUGIN_NAME = "TestPaymentGatewayApiControlPlugin";
 
@@ -234,7 +235,7 @@ public class TestPaymentGatewayApiWithPaymentControl extends PaymentTestSuiteNoD
 
         @Override
         public PriorPaymentControlResult priorCall(final PaymentControlContext paymentControlContext, final Iterable<PluginProperty> properties) throws PaymentControlApiException {
-            return new DefaultPriorPaymentControlResult(false, null, null, null, getAdjustedProperties(properties, newPriorCallProperties, removedPriorCallProperties));
+            return new DefaultPriorPaymentControlResult(false, account.getPaymentMethodId(), null, null, getAdjustedProperties(properties, newPriorCallProperties, removedPriorCallProperties));
         }
 
         @Override
@@ -247,7 +248,7 @@ public class TestPaymentGatewayApiWithPaymentControl extends PaymentTestSuiteNoD
             return new DefaultFailureCallResult(null, getAdjustedProperties(properties, newOnResultProperties, removedOnResultProperties));
         }
 
-        private static Iterable<PluginProperty> getAdjustedProperties(final Iterable<PluginProperty> input, final Iterable<PluginProperty> newProperties, final Iterable<PluginProperty> removedProperties) {
+        private Iterable<PluginProperty> getAdjustedProperties(final Iterable<PluginProperty> input, final Iterable<PluginProperty> newProperties, final Iterable<PluginProperty> removedProperties) {
             final Iterable<PluginProperty> filtered = Iterables.filter(input, new Predicate<PluginProperty>() {
                 @Override
                 public boolean apply(final PluginProperty p) {
diff --git a/payment/src/test/java/org/killbill/billing/payment/core/TestPaymentMethodProcessorWithDB.java b/payment/src/test/java/org/killbill/billing/payment/core/TestPaymentMethodProcessorWithDB.java
new file mode 100644
index 0000000..79f4b34
--- /dev/null
+++ b/payment/src/test/java/org/killbill/billing/payment/core/TestPaymentMethodProcessorWithDB.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.payment.core;
+
+import java.util.UUID;
+
+import org.killbill.billing.account.api.Account;
+import org.killbill.billing.payment.PaymentTestSuiteWithEmbeddedDB;
+import org.killbill.billing.payment.api.PaymentApiException;
+import org.killbill.billing.payment.api.PluginProperty;
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+import com.google.common.collect.ImmutableList;
+
+public class TestPaymentMethodProcessorWithDB extends PaymentTestSuiteWithEmbeddedDB {
+
+    private static final ImmutableList<PluginProperty> PLUGIN_PROPERTIES = ImmutableList.<PluginProperty>of();
+
+    @Test(groups = "slow", expectedExceptions = PaymentApiException.class, expectedExceptionsMessageRegExp = ".*Payment method .* has a different account id")
+    public void testSetDefaultPaymentMethodDifferentAccount() throws Exception {
+        final Account account = testHelper.createTestAccount("foo@bar.com", true);
+        final Account secondAccount = testHelper.createTestAccount("foo2@bar.com", true);
+
+        // Add new payment method
+        final UUID newPaymentMethod = paymentMethodProcessor.createOrGetExternalPaymentMethod("pmExternalKey", secondAccount, PLUGIN_PROPERTIES, callContext, internalCallContext);
+
+        paymentMethodProcessor.setDefaultPaymentMethod(account, newPaymentMethod, PLUGIN_PROPERTIES, callContext, internalCallContext);
+    }
+
+    @Test(groups = "slow")
+    public void testSetDefaultPaymentMethodSameAccount() throws Exception {
+        final Account account = testHelper.createTestAccount("foo@bar.com", true);
+
+        // Add new payment method
+        final UUID newPaymentMethod = paymentMethodProcessor.createOrGetExternalPaymentMethod("pmExternalKey", account, PLUGIN_PROPERTIES, callContext, internalCallContext);
+
+        paymentMethodProcessor.setDefaultPaymentMethod(account, newPaymentMethod, PLUGIN_PROPERTIES, callContext, internalCallContext);
+
+        final Account accountById = accountApi.getAccountById(account.getId(), internalCallContext);
+        Assert.assertEquals(accountById.getPaymentMethodId(), newPaymentMethod);
+    }
+}
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 93e85a2..94d024a 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
@@ -498,7 +498,6 @@ public class TestPaymentDao extends PaymentTestSuiteWithEmbeddedDB {
 
         final Iterator<PaymentTransactionModelDao> iterator = result.iterator();
         for (int i = 0; i < NB_ENTRIES; i++) {
-            System.out.println("i = " + i);
             Assert.assertTrue(iterator.hasNext());
             final PaymentTransactionModelDao nextEntry = iterator.next();
             Assert.assertEquals(nextEntry.getTransactionStatus(), TransactionStatus.UNKNOWN);
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 9eadf60..177d729 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
@@ -57,7 +57,7 @@ public interface PaymentConfig extends KillbillConfig {
     TimeSpan getPaymentPluginTimeout();
 
     @Config("org.killbill.payment.plugin.threads.nb")
-    @Default("10")
+    @Default("100")
     @Description("Number of threads for plugin executor dispatcher")
     int getPaymentPluginThreadNb();
 
diff --git a/util/src/test/java/org/killbill/billing/mock/MockPlan.java b/util/src/test/java/org/killbill/billing/mock/MockPlan.java
index a5e03ec..0b5e92d 100644
--- a/util/src/test/java/org/killbill/billing/mock/MockPlan.java
+++ b/util/src/test/java/org/killbill/billing/mock/MockPlan.java
@@ -21,7 +21,6 @@ import java.util.Iterator;
 import java.util.UUID;
 
 import org.joda.time.DateTime;
-
 import org.killbill.billing.catalog.api.BillingPeriod;
 import org.killbill.billing.catalog.api.CatalogApiException;
 import org.killbill.billing.catalog.api.PhaseType;
@@ -93,11 +92,6 @@ public class MockPlan implements Plan {
     }
 
     @Override
-    public boolean isRetired() {
-        return false;
-    }
-
-    @Override
     public DateTime dateOfFirstRecurringNonZeroCharge(final DateTime subscriptionStartDate, PhaseType initialPhaseType) {
         throw new UnsupportedOperationException();
     }
diff --git a/util/src/test/java/org/killbill/billing/mock/MockPriceList.java b/util/src/test/java/org/killbill/billing/mock/MockPriceList.java
index 0cbe34f..3fddaf9 100644
--- a/util/src/test/java/org/killbill/billing/mock/MockPriceList.java
+++ b/util/src/test/java/org/killbill/billing/mock/MockPriceList.java
@@ -25,25 +25,18 @@ import org.killbill.billing.catalog.api.Product;
 
 public class MockPriceList implements PriceList {
     private final String name;
-    private final Boolean isRetired;
     private final Plan plan;
 
     public MockPriceList() {
-        this(false, UUID.randomUUID().toString(), new MockPlan());
+        this(UUID.randomUUID().toString(), new MockPlan());
     }
 
-    public MockPriceList(final Boolean retired, final String name, final Plan plan) {
-        isRetired = retired;
+    public MockPriceList(final String name, final Plan plan) {
         this.name = name;
         this.plan = plan;
     }
 
     @Override
-    public boolean isRetired() {
-        return isRetired;
-    }
-
-    @Override
     public String getName() {
         return name;
     }
diff --git a/util/src/test/java/org/killbill/billing/mock/MockProduct.java b/util/src/test/java/org/killbill/billing/mock/MockProduct.java
index 3eec119..046c18a 100644
--- a/util/src/test/java/org/killbill/billing/mock/MockProduct.java
+++ b/util/src/test/java/org/killbill/billing/mock/MockProduct.java
@@ -60,11 +60,6 @@ public class MockProduct implements Product {
     }
 
     @Override
-    public boolean isRetired() {
-        throw new UnsupportedOperationException();
-    }
-
-    @Override
     public Product[] getAvailable() {
         return available;
     }