killbill-aplcache

Changes

.travis.yml 5(+5 -0)

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

junction/pom.xml 19(+12 -7)

pom.xml 20(+13 -7)

Details

.travis.yml 5(+5 -0)

diff --git a/.travis.yml b/.travis.yml
index 93b1a6e..3f1b576 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -4,3 +4,8 @@ script: mvn clean install -Ptravis
 notifications:
   email:
     - ri-dev@ning.com
+
+jdk:
+  - openjdk6
+  - openjdk7
+  - oraclejdk7
diff --git a/analytics/src/main/java/com/ning/billing/analytics/BusinessAccountRecorder.java b/analytics/src/main/java/com/ning/billing/analytics/BusinessAccountRecorder.java
index eb68910..acd4d8f 100644
--- a/analytics/src/main/java/com/ning/billing/analytics/BusinessAccountRecorder.java
+++ b/analytics/src/main/java/com/ning/billing/analytics/BusinessAccountRecorder.java
@@ -193,7 +193,6 @@ public class BusinessAccountRecorder {
                     }
                 }
             }
-            
 
             bac.setLastPaymentStatus(lastPaymentStatus);
             bac.setPaymentMethod(paymentMethod);
diff --git a/api/src/main/java/com/ning/billing/catalog/api/StaticCatalog.java b/api/src/main/java/com/ning/billing/catalog/api/StaticCatalog.java
index 17ea132..0b2ef57 100644
--- a/api/src/main/java/com/ning/billing/catalog/api/StaticCatalog.java
+++ b/api/src/main/java/com/ning/billing/catalog/api/StaticCatalog.java
@@ -17,6 +17,7 @@
 package com.ning.billing.catalog.api;
 
 import java.util.Date;
+import java.util.List;
 
 
 public interface StaticCatalog {
@@ -76,4 +77,6 @@ public interface StaticCatalog {
 
     public abstract boolean canCreatePlan(PlanSpecifier specifier) throws CatalogApiException;
 
+    public abstract List<Listing> getAvailableAddonListings(String baseProductName) throws CatalogApiException;
+
 }
\ No newline at end of file
diff --git a/api/src/main/java/com/ning/billing/ErrorCode.java b/api/src/main/java/com/ning/billing/ErrorCode.java
index 1ff2610..7816bee 100644
--- a/api/src/main/java/com/ning/billing/ErrorCode.java
+++ b/api/src/main/java/com/ning/billing/ErrorCode.java
@@ -17,9 +17,6 @@
 package com.ning.billing;
 
 public enum ErrorCode {
-
-
-    
     /*
      * Range 0 : COMMON EXCEPTIONS
      */
@@ -185,7 +182,17 @@ public enum ErrorCode {
     INVOICE_NO_ACCOUNT_ID_FOR_SUBSCRIPTION_ID(4003, "No account id was retrieved for subscription id %s"),
     INVOICE_INVALID_DATE_SEQUENCE(4004, "Date sequence was invalid. Start Date: %s; End Date: %s; Target Date: %s"),
     INVOICE_TARGET_DATE_TOO_FAR_IN_THE_FUTURE(4005, "The target date was too far in the future. Target Date: %s"),
-    
+
+    /*
+     *
+     * Range 4900: Invoice payment
+     *
+     */
+    INVOICE_PAYMENT_NOT_FOUND(4900, "No invoice payment could be found for id %s."),
+    CHARGE_BACK_AMOUNT_TOO_HIGH(4901, "Tried to charge back %s of a %s payment."),
+    CHARGE_BACK_AMOUNT_IS_NEGATIVE(4902, "Charge backs for negative amounts are not permitted"),
+    CHARGE_BACK_COULD_NOT_FIND_ACCOUNT_ID(4093, "Could not find chargeback for id %s."),
+    CHARGE_BACK_DOES_NOT_EXIST(4093, "Could not find chargeback for id %s."),
     /*
      * 
      * Range 5000: Overdue system
@@ -228,9 +235,11 @@ public enum ErrorCode {
     PAYMENT_NULL_INVOICE(7017, "Invoice %s has a balance <= 0 "),      
     PAYMENT_AMOUNT_DENIED(7018, "Payment amount requested for invoice %s is greater than invoice balance [%f/%f]"),         
     PAYMENT_INTERNAL_ERROR(7019, "Internal payment error : %s"),
+    PAYMENT_NO_SUCH_PAYMENT(7020, "Payment %s does not exist"),
     
     PAYMENT_PLUGIN_TIMEOUT(7100, "Plugin timeout for account %s and invoice %s"),    
     PAYMENT_PLUGIN_ACCOUNT_INIT(7101, "Account initialization for account %s and plugin % s failed: %s"),        
+
     
     /*
     *
diff --git a/api/src/main/java/com/ning/billing/invoice/api/Invoice.java b/api/src/main/java/com/ning/billing/invoice/api/Invoice.java
index 9f21b11..bf7fd7b 100644
--- a/api/src/main/java/com/ning/billing/invoice/api/Invoice.java
+++ b/api/src/main/java/com/ning/billing/invoice/api/Invoice.java
@@ -57,7 +57,9 @@ public interface Invoice extends Entity {
 
     BigDecimal getAmountPaid();
 
-    BigDecimal getTotalAmount();
+    BigDecimal getAmountCharged();
+
+    BigDecimal getAmountCredited();
 
     BigDecimal getBalance();
 
diff --git a/api/src/main/java/com/ning/billing/invoice/api/InvoiceItem.java b/api/src/main/java/com/ning/billing/invoice/api/InvoiceItem.java
index cd7decd..9f898c7 100644
--- a/api/src/main/java/com/ning/billing/invoice/api/InvoiceItem.java
+++ b/api/src/main/java/com/ning/billing/invoice/api/InvoiceItem.java
@@ -46,5 +46,7 @@ public interface InvoiceItem extends Entity, Comparable<InvoiceItem> {
 
     Currency getCurrency();
 
+    InvoiceItemType getInvoiceItemType();
+
     InvoiceItem asReversingItem();
 }
diff --git a/api/src/main/java/com/ning/billing/invoice/api/InvoiceItemType.java b/api/src/main/java/com/ning/billing/invoice/api/InvoiceItemType.java
index ce066a4..aaae9df 100644
--- a/api/src/main/java/com/ning/billing/invoice/api/InvoiceItemType.java
+++ b/api/src/main/java/com/ning/billing/invoice/api/InvoiceItemType.java
@@ -23,6 +23,5 @@ public enum InvoiceItemType {
     MIGRATION,
     REFUND,
     CHARGE_BACK,
-    ADD_CREDIT,
-    USE_CREDIT
+    CREDIT
 }
diff --git a/api/src/main/java/com/ning/billing/invoice/api/InvoicePayment.java b/api/src/main/java/com/ning/billing/invoice/api/InvoicePayment.java
index 29f175b..4ba42c1 100644
--- a/api/src/main/java/com/ning/billing/invoice/api/InvoicePayment.java
+++ b/api/src/main/java/com/ning/billing/invoice/api/InvoicePayment.java
@@ -32,4 +32,11 @@ public interface InvoicePayment extends Entity {
     BigDecimal getAmount();
 
     Currency getCurrency();
+
+    UUID getReversedInvoicePaymentId();
+
+    /*
+     * @param chargeBackAmount BigDecimal pass the amount as a positive number
+     */
+    InvoicePayment asChargeBack(BigDecimal chargeBackAmount, DateTime chargeBackDate) throws InvoiceApiException;
 }
diff --git a/api/src/main/java/com/ning/billing/invoice/api/InvoicePaymentApi.java b/api/src/main/java/com/ning/billing/invoice/api/InvoicePaymentApi.java
index 0a34604..ecc477b 100644
--- a/api/src/main/java/com/ning/billing/invoice/api/InvoicePaymentApi.java
+++ b/api/src/main/java/com/ning/billing/invoice/api/InvoicePaymentApi.java
@@ -26,7 +26,6 @@ import org.joda.time.DateTime;
 import com.ning.billing.catalog.api.Currency;
 
 public interface InvoicePaymentApi {
-
     /**
      * @param accountId
      * @return All invoices, including migrated invoices
@@ -45,4 +44,17 @@ public interface InvoicePaymentApi {
     
     public void notifyOfPaymentAttempt(UUID invoiceId, UUID paymentAttemptId, DateTime paymentAttemptDate, CallContext context);
 
+    public void processChargeback(UUID invoicePaymentId, BigDecimal amount, CallContext context) throws InvoiceApiException;
+
+    public void processChargeback(UUID invoicePaymentId, CallContext context) throws InvoiceApiException;
+
+    public BigDecimal getRemainingAmountPaid(UUID invoicePaymentId);
+
+    public List<InvoicePayment> getChargebacksByAccountId(UUID accountId);
+
+    public UUID getAccountIdFromInvoicePaymentId(UUID uuid) throws InvoiceApiException;
+
+    public List<InvoicePayment> getChargebacksByPaymentAttemptId(UUID paymentAttemptId);
+
+    public InvoicePayment getChargebackById(UUID chargebackId) throws InvoiceApiException;
 }
diff --git a/api/src/main/java/com/ning/billing/invoice/api/InvoiceUserApi.java b/api/src/main/java/com/ning/billing/invoice/api/InvoiceUserApi.java
index 66016df..56ee93f 100644
--- a/api/src/main/java/com/ning/billing/invoice/api/InvoiceUserApi.java
+++ b/api/src/main/java/com/ning/billing/invoice/api/InvoiceUserApi.java
@@ -16,6 +16,7 @@
 
 package com.ning.billing.invoice.api;
 
+import com.ning.billing.catalog.api.Currency;
 import com.ning.billing.util.callcontext.CallContext;
 import org.joda.time.DateTime;
 
@@ -41,5 +42,10 @@ public interface InvoiceUserApi {
 
     public void tagInvoiceAsWrittenOff(UUID invoiceId, CallContext context);
 
-    public void tagInvoiceAsNotWrittenOff(UUID invoiceId, CallContext context);
+    public void tagInvoiceAsNotWrittenOff(UUID invoiceId, CallContext context) throws InvoiceApiException;
+
+    public InvoiceItem getCreditById(UUID creditId) throws InvoiceApiException;
+
+    public InvoiceItem insertCredit( UUID accountId,  BigDecimal amount,  DateTime effectiveDate,
+                              Currency currency,  CallContext context) throws InvoiceApiException;
 }
diff --git a/api/src/main/java/com/ning/billing/junction/api/BillingApi.java b/api/src/main/java/com/ning/billing/junction/api/BillingApi.java
index cff8ee7..bb38e8d 100644
--- a/api/src/main/java/com/ning/billing/junction/api/BillingApi.java
+++ b/api/src/main/java/com/ning/billing/junction/api/BillingApi.java
@@ -16,10 +16,8 @@
 
 package com.ning.billing.junction.api;
 
-import java.util.SortedSet;
 import java.util.UUID;
 
-import com.ning.billing.entitlement.api.billing.BillingEvent;
 import com.ning.billing.entitlement.api.billing.ChargeThruApi;
 
 public interface BillingApi extends ChargeThruApi {
@@ -29,5 +27,5 @@ public interface BillingApi extends ChargeThruApi {
      * @return an ordered list of billing event for the given accounts
      *
      */
-    public SortedSet<BillingEvent> getBillingEventsForAccountAndUpdateAccountBCD(UUID accountId);
+    public BillingEventSet getBillingEventsForAccountAndUpdateAccountBCD(UUID accountId);
 }
diff --git a/api/src/main/java/com/ning/billing/junction/api/BillingEventSet.java b/api/src/main/java/com/ning/billing/junction/api/BillingEventSet.java
index 3a7b66d..aed5c6a 100644
--- a/api/src/main/java/com/ning/billing/junction/api/BillingEventSet.java
+++ b/api/src/main/java/com/ning/billing/junction/api/BillingEventSet.java
@@ -27,5 +27,7 @@ public interface BillingEventSet extends SortedSet<BillingEvent> {
     public abstract boolean isAccountAutoInvoiceOff();
 
     public abstract List<UUID> getSubscriptionIdsWithAutoInvoiceOff();
+    
+    public boolean isLast(BillingEvent event);
 
 }
\ No newline at end of file
diff --git a/api/src/main/java/com/ning/billing/payment/api/PaymentApi.java b/api/src/main/java/com/ning/billing/payment/api/PaymentApi.java
index d2661f3..03954eb 100644
--- a/api/src/main/java/com/ning/billing/payment/api/PaymentApi.java
+++ b/api/src/main/java/com/ning/billing/payment/api/PaymentApi.java
@@ -42,6 +42,8 @@ public interface PaymentApi {
    public List<Payment> getAccountPayments(final UUID accountId)
     throws PaymentApiException;
    
+   public Payment getPayment(final UUID paymentId)
+   throws PaymentApiException;
    
    /*
     * Payment method Apis
@@ -71,6 +73,5 @@ public interface PaymentApi {
    
    public void setDefaultPaymentMethod(final Account account, final UUID paymentMethodId, final CallContext context)
    throws PaymentApiException;
-   
-  
+
 }
diff --git a/api/src/main/java/com/ning/billing/util/api/TagUserApi.java b/api/src/main/java/com/ning/billing/util/api/TagUserApi.java
index 16b8a30..fed7517 100644
--- a/api/src/main/java/com/ning/billing/util/api/TagUserApi.java
+++ b/api/src/main/java/com/ning/billing/util/api/TagUserApi.java
@@ -20,6 +20,7 @@ import java.util.List;
 import java.util.Map;
 import java.util.UUID;
 
+import com.ning.billing.invoice.api.InvoiceApiException;
 import com.ning.billing.util.callcontext.CallContext;
 
 import com.ning.billing.util.dao.ObjectType;
diff --git a/api/src/main/java/com/ning/billing/util/tag/ControlTagType.java b/api/src/main/java/com/ning/billing/util/tag/ControlTagType.java
index c122766..65d32c7 100644
--- a/api/src/main/java/com/ning/billing/util/tag/ControlTagType.java
+++ b/api/src/main/java/com/ning/billing/util/tag/ControlTagType.java
@@ -48,7 +48,9 @@ public enum ControlTagType {
 
     public TagDefinition toTagDefinition() {
         return new TagDefinition() {
-            @Override public String getName() {return this.toString();}
+            @Override public String getName() {
+                return name();
+            }
 
             @Override public String getDescription() {return description;}
 

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

diff --git a/beatrix/pom.xml b/beatrix/pom.xml
index a81c264..547db04 100644
--- a/beatrix/pom.xml
+++ b/beatrix/pom.xml
@@ -111,6 +111,7 @@
             <artifactId>jdbi</artifactId>
             <scope>test</scope>
         </dependency>
+
         <dependency>
             <groupId>org.testng</groupId>
             <artifactId>testng</artifactId>
diff --git a/beatrix/src/test/java/com/ning/billing/beatrix/integration/overdue/TestOverdueIntegration.java b/beatrix/src/test/java/com/ning/billing/beatrix/integration/overdue/TestOverdueIntegration.java
index dcdc17b..1d48692 100644
--- a/beatrix/src/test/java/com/ning/billing/beatrix/integration/overdue/TestOverdueIntegration.java
+++ b/beatrix/src/test/java/com/ning/billing/beatrix/integration/overdue/TestOverdueIntegration.java
@@ -187,7 +187,6 @@ public class TestOverdueIntegration extends TestIntegrationBase {
     
         clock.addDays(15); // DAY 45 - 15 days after invoice
         assertTrue(busHandler.isCompleted(DELAY));
-        overdueApi.refreshOverdueStateFor(bundle); // trigger a refresh because there are no events to do it for us
         //should still be in clear state
         checkODState(BlockingApi.CLEAR_STATE_NAME);
 
@@ -200,14 +199,12 @@ public class TestOverdueIntegration extends TestIntegrationBase {
           
         clock.addDays(2); //DAY 67 - 37 days after invoice
         assertTrue(busHandler.isCompleted(DELAY));
-        overdueApi.refreshOverdueStateFor(bundle); // trigger a refresh because there are no events to do it for us
         // should still be in OD1
         checkODState("OD1");
 
         //busHandler.pushExpectedEvents(NextEvent.INVOICE, NextEvent.PAYMENT_ERROR);
         clock.addDays(8); //DAY 75 - 45 days after invoice
         assertTrue(busHandler.isCompleted(DELAY));
-        overdueApi.refreshOverdueStateFor(bundle); // trigger a refresh because there are no events to do it for us
         // should still be in OD1
         checkODState("OD2");
         
@@ -229,7 +226,6 @@ public class TestOverdueIntegration extends TestIntegrationBase {
             }
         }
         
-        overdueApi.refreshOverdueStateFor(bundle);
         checkODState(BlockingApi.CLEAR_STATE_NAME);
 
     }
@@ -240,6 +236,7 @@ public class TestOverdueIntegration extends TestIntegrationBase {
             await().atMost(10, SECONDS).until(new Callable<Boolean>() {
                 @Override
                 public Boolean call() throws Exception {
+                    overdueApi.refreshOverdueStateFor(bundle); 
                     return expected.equals(blockingApi.getBlockingStateFor(bundle).getStateName()) ;
                 }
             });
diff --git a/beatrix/src/test/java/com/ning/billing/beatrix/integration/TestIntegrationWithAutoInvoiceOffTag.java b/beatrix/src/test/java/com/ning/billing/beatrix/integration/TestIntegrationWithAutoInvoiceOffTag.java
new file mode 100644
index 0000000..bc4f5a9
--- /dev/null
+++ b/beatrix/src/test/java/com/ning/billing/beatrix/integration/TestIntegrationWithAutoInvoiceOffTag.java
@@ -0,0 +1,175 @@
+/*
+ * Copyright 2010-2011 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.
+ */
+
+package com.ning.billing.beatrix.integration;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNotNull;
+import static org.testng.Assert.assertTrue;
+
+import java.util.Collection;
+import java.util.Map;
+import java.util.UUID;
+
+import org.joda.time.DateTime;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Guice;
+import org.testng.annotations.Test;
+
+import com.google.inject.Inject;
+import com.ning.billing.account.api.Account;
+import com.ning.billing.api.TestApiListener.NextEvent;
+import com.ning.billing.catalog.api.BillingPeriod;
+import com.ning.billing.catalog.api.PlanPhaseSpecifier;
+import com.ning.billing.catalog.api.PriceListSet;
+import com.ning.billing.catalog.api.ProductCategory;
+import com.ning.billing.entitlement.api.user.SubscriptionBundle;
+import com.ning.billing.entitlement.api.user.SubscriptionData;
+import com.ning.billing.invoice.api.Invoice;
+import com.ning.billing.invoice.api.InvoiceUserApi;
+import com.ning.billing.util.api.TagDefinitionApiException;
+import com.ning.billing.util.api.TagUserApi;
+import com.ning.billing.util.dao.ObjectType;
+import com.ning.billing.util.tag.ControlTagType;
+import com.ning.billing.util.tag.Tag;
+import com.ning.billing.util.tag.TagDefinition;
+
+@Guice(modules = {BeatrixModule.class})
+public class TestIntegrationWithAutoInvoiceOffTag extends TestIntegrationBase {
+
+    @Inject
+    private InvoiceUserApi invoiceApi;
+
+    @Inject
+    private TagUserApi tagApi;
+
+    private Account account;
+    private SubscriptionBundle bundle;
+    private String productName;
+    private BillingPeriod term;
+    private String planSetName;
+
+    @BeforeMethod(groups = {"slow"})
+    public void setupOverdue() throws Exception {
+        
+        account = accountUserApi.createAccount(getAccountData(25), null, null, context);
+        assertNotNull(account);
+
+        bundle = entitlementUserApi.createBundleForAccount(account.getId(), "whatever", context);
+
+        productName = "Shotgun";
+        term = BillingPeriod.MONTHLY;
+        planSetName = PriceListSet.DEFAULT_PRICELIST_NAME;
+    }
+
+    @Test(groups={"slow"}, enabled = true)
+    public void testAutoInvoiceOffAccount() throws Exception {
+        clock.setTime(new DateTime(2012, 5, 1, 0, 3, 42, 0));
+        addTag(account.getId(), ObjectType.ACCOUNT);
+       
+        // set next invoice to fail and create network 
+        busHandler.pushExpectedEvents(NextEvent.CREATE);
+        SubscriptionData baseSubscription = subscriptionDataFromSubscription(entitlementUserApi.createSubscription(bundle.getId(),
+                new PlanPhaseSpecifier(productName, ProductCategory.BASE, term, planSetName, null), null, context));
+        assertNotNull(baseSubscription);
+        assertTrue(busHandler.isCompleted(DELAY));
+
+ 
+        Collection<Invoice> invoices = invoiceApi.getInvoicesByAccount(account.getId());       
+        assertEquals(invoices.size(), 0);
+ 
+        clock.addDays(10); // DAY 10 still in trial
+        assertTrue(busHandler.isCompleted(DELAY));
+
+        invoices = invoiceApi.getInvoicesByAccount(account.getId());       
+        assertEquals(invoices.size(), 0);
+
+        busHandler.pushExpectedEvents(NextEvent.PHASE);
+        clock.addDays(30); // DAY 40 out of trial
+        assertTrue(busHandler.isCompleted(DELAY));
+
+        invoices = invoiceApi.getInvoicesByAccount(account.getId());       
+        assertEquals(invoices.size(), 0);
+
+    }
+    
+    @Test(groups={"slow"}, enabled = true)
+    public void testAutoInvoiceOffSingleSubscription() throws Exception {
+        clock.setTime(new DateTime(2012, 5, 1, 0, 3, 42, 0));
+       
+        // set next invoice to fail and create network 
+        busHandler.pushExpectedEvents(NextEvent.CREATE,NextEvent.INVOICE);
+        SubscriptionData baseSubscription = subscriptionDataFromSubscription(entitlementUserApi.createSubscription(bundle.getId(),
+                new PlanPhaseSpecifier(productName, ProductCategory.BASE, term, planSetName, null), null, context));
+        assertNotNull(baseSubscription);
+        assertTrue(busHandler.isCompleted(DELAY));
+        
+        Collection<Invoice> invoices = invoiceApi.getInvoicesByAccount(account.getId());       
+        assertEquals(invoices.size(), 1); // first invoice is generated immediately after creation can't reliably stop it
+ 
+
+        addTag(baseSubscription.getBundleId(), ObjectType.BUNDLE);
+
+        busHandler.pushExpectedEvents(NextEvent.PHASE);
+        clock.addDays(40); // DAY 40 out of trial
+        assertTrue(busHandler.isCompleted(DELAY));
+
+        invoices = invoiceApi.getInvoicesByAccount(account.getId());       
+        assertEquals(invoices.size(), 1); //No additional invoices generated
+
+    }
+
+    
+    @Test(groups={"slow"}, enabled = true)
+    public void testAutoInvoiceOffMultipleSubscriptions() throws Exception {
+        clock.setTime(new DateTime(2012, 5, 1, 0, 3, 42, 0));
+       
+        // set next invoice to fail and create network 
+        busHandler.pushExpectedEvents(NextEvent.CREATE,NextEvent.INVOICE);
+        SubscriptionData baseSubscription = subscriptionDataFromSubscription(entitlementUserApi.createSubscription(bundle.getId(),
+                new PlanPhaseSpecifier(productName, ProductCategory.BASE, term, planSetName, null), null, context));
+        assertNotNull(baseSubscription);
+        assertTrue(busHandler.isCompleted(DELAY));
+ 
+        SubscriptionBundle bundle2 = entitlementUserApi.createBundleForAccount(account.getId(), "whatever", context);
+
+        busHandler.pushExpectedEvents(NextEvent.CREATE,NextEvent.INVOICE);
+        SubscriptionData baseSubscription2 = subscriptionDataFromSubscription(entitlementUserApi.createSubscription(bundle2.getId(),
+                new PlanPhaseSpecifier(productName, ProductCategory.BASE, term, planSetName, null), null, context));
+        assertNotNull(baseSubscription2);
+        assertTrue(busHandler.isCompleted(DELAY));
+
+        Collection<Invoice> invoices = invoiceApi.getInvoicesByAccount(account.getId());       
+        assertEquals(invoices.size(), 2); // first invoice is generated immediately after creation can't reliably stop it
+ 
+        addTag(baseSubscription.getBundleId(), ObjectType.BUNDLE);
+
+        busHandler.pushExpectedEvents(NextEvent.PHASE,NextEvent.PHASE,NextEvent.INVOICE);
+        clock.addDays(40); // DAY 40 out of trial
+        assertTrue(busHandler.isCompleted(DELAY));
+
+        invoices = invoiceApi.getInvoicesByAccount(account.getId());       
+        assertEquals(invoices.size(), 3); // Only one additional invoice generated
+    }
+
+
+    private void addTag(UUID id, ObjectType type) throws TagDefinitionApiException {
+        TagDefinition def = tagApi.getTagDefinition(ControlTagType.AUTO_INVOICING_OFF.name());
+        tagApi.addTag(id, type, def, context);
+        Map<String,Tag> tags = tagApi.getTags(id, type);
+        assertNotNull(tags.get(ControlTagType.AUTO_INVOICING_OFF.name()));
+    }
+}
diff --git a/beatrix/src/test/resources/log4j.xml b/beatrix/src/test/resources/log4j.xml
index ac530a1..79e0ec2 100644
--- a/beatrix/src/test/resources/log4j.xml
+++ b/beatrix/src/test/resources/log4j.xml
@@ -33,6 +33,10 @@
         <level value="info"/>
     </logger>
 
+    <logger name="com.ning.billing.junction">
+        <level value="debug"/>
+    </logger>
+
     <root>
         <priority value="info"/>
         <appender-ref ref="stdout"/>
diff --git a/catalog/src/main/java/com/ning/billing/catalog/DefaultDuration.java b/catalog/src/main/java/com/ning/billing/catalog/DefaultDuration.java
index 71bfe50..34860be 100644
--- a/catalog/src/main/java/com/ning/billing/catalog/DefaultDuration.java
+++ b/catalog/src/main/java/com/ning/billing/catalog/DefaultDuration.java
@@ -16,16 +16,18 @@
 
 package com.ning.billing.catalog;
 
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlElement;
+
+import org.joda.time.DateTime;
+import org.joda.time.Period;
+
 import com.ning.billing.catalog.api.Duration;
 import com.ning.billing.catalog.api.TimeUnit;
 import com.ning.billing.util.config.ValidatingConfig;
+import com.ning.billing.util.config.ValidationError;
 import com.ning.billing.util.config.ValidationErrors;
-import org.joda.time.DateTime;
-import org.joda.time.Period;
-
-import javax.xml.bind.annotation.XmlAccessType;
-import javax.xml.bind.annotation.XmlAccessorType;
-import javax.xml.bind.annotation.XmlElement;
 
 @XmlAccessorType(XmlAccessType.NONE)
 public class DefaultDuration extends ValidatingConfig<StandaloneCatalog> implements Duration {
@@ -71,6 +73,12 @@ public class DefaultDuration extends ValidatingConfig<StandaloneCatalog> impleme
 
     @Override
 	public ValidationErrors validate(StandaloneCatalog catalog, ValidationErrors errors) {
+        //Validation: TimeUnit UNLIMITED iff number == -1
+        if((unit == TimeUnit.UNLIMITED && number != -1)) {
+            errors.add(new ValidationError("Duration can only have 'UNLIMITED' unit if the number is omitted.", 
+                    catalog.getCatalogURI(), DefaultPlanPhase.class, ""));
+        }
+
 		//TODO MDW - Validation TimeUnit UNLIMITED iff number == -1
 		return errors;
 	}
diff --git a/catalog/src/main/java/com/ning/billing/catalog/DefaultPlanPhase.java b/catalog/src/main/java/com/ning/billing/catalog/DefaultPlanPhase.java
index 0f021c3..8feff37 100644
--- a/catalog/src/main/java/com/ning/billing/catalog/DefaultPlanPhase.java
+++ b/catalog/src/main/java/com/ning/billing/catalog/DefaultPlanPhase.java
@@ -144,20 +144,26 @@ public class DefaultPlanPhase extends ValidatingConfig<StandaloneCatalog> implem
 					catalog.getCatalogURI(), DefaultPlanPhase.class, type.toString()));
 		}
 
-		//Validation: if there is no recurring price there should be no billing period
-		if((recurringPrice == null) && billingPeriod != BillingPeriod.NO_BILLING_PERIOD) {
-			errors.add(new ValidationError(String.format("Phase %s of plan %s has no recurring price but does have a billing period. The billing period should be set to '%s'",
-					type.toString(), plan.getName(), BillingPeriod.NO_BILLING_PERIOD), 
-					catalog.getCatalogURI(), DefaultPlanPhase.class, type.toString()));
-		}
-		
+        //Validation: if there is no recurring price there should be no billing period
+        if((recurringPrice == null) && billingPeriod != BillingPeriod.NO_BILLING_PERIOD) {
+            errors.add(new ValidationError(String.format("Phase %s of plan %s has no recurring price but does have a billing period. The billing period should be set to '%s'",
+                    type.toString(), plan.getName(), BillingPeriod.NO_BILLING_PERIOD), 
+                    catalog.getCatalogURI(), DefaultPlanPhase.class, type.toString()));
+        }
+        
+        //Validation: if there BP is set to NO_BILLING_PERIOD there must be a fixed price
+        if((billingPeriod == BillingPeriod.NO_BILLING_PERIOD && fixedPrice == null)) {
+            errors.add(new ValidationError(String.format("Phase %s of plan %s has no billing period. It must have a fixed price set.",
+                    type.toString(), plan.getName()), 
+                    catalog.getCatalogURI(), DefaultPlanPhase.class, type.toString()));
+        }
+        
 		//Validation: there must be at least one of recurringPrice or fixedPrice
 		if((recurringPrice == null) && fixedPrice == null) {
 			errors.add(new ValidationError(String.format("Phase %s of plan %s has neither a recurring price or a fixed price.",
 					type.toString(), plan.getName()), 
 					catalog.getCatalogURI(), DefaultPlanPhase.class, type.toString()));
 		}
-		//TODO : if there BP is set to NO_BILLING_PERIOD there must be a recurring price
         return errors;
 	}
 	
diff --git a/catalog/src/main/java/com/ning/billing/catalog/DefaultPriceListSet.java b/catalog/src/main/java/com/ning/billing/catalog/DefaultPriceListSet.java
index 83d810b..b8a8695 100644
--- a/catalog/src/main/java/com/ning/billing/catalog/DefaultPriceListSet.java
+++ b/catalog/src/main/java/com/ning/billing/catalog/DefaultPriceListSet.java
@@ -16,6 +16,9 @@
 
 package com.ning.billing.catalog;
 
+import java.util.ArrayList;
+import java.util.List;
+
 import javax.xml.bind.annotation.XmlAccessType;
 import javax.xml.bind.annotation.XmlAccessorType;
 import javax.xml.bind.annotation.XmlElement;
@@ -23,6 +26,7 @@ import javax.xml.bind.annotation.XmlElement;
 import com.ning.billing.ErrorCode;
 import com.ning.billing.catalog.api.BillingPeriod;
 import com.ning.billing.catalog.api.CatalogApiException;
+import com.ning.billing.catalog.api.PriceList;
 import com.ning.billing.catalog.api.PriceListSet;
 import com.ning.billing.catalog.api.Product;
 import com.ning.billing.util.config.ValidatingConfig;
@@ -99,6 +103,15 @@ public class DefaultPriceListSet extends ValidatingConfig<StandaloneCatalog> {
 		return childPriceLists;
 	}
 
+    public List<PriceList> getAllPriceLists() {
+        List<PriceList> result = new ArrayList<PriceList> (childPriceLists.length + 1);
+        result.add(getDefaultPricelist());
+        for(PriceList list : getChildPriceLists()) {
+            result.add(list);
+        }
+        return result;
+    }
+
 
 	
 }
diff --git a/catalog/src/main/java/com/ning/billing/catalog/StandaloneCatalog.java b/catalog/src/main/java/com/ning/billing/catalog/StandaloneCatalog.java
index a63645c..32324f9 100644
--- a/catalog/src/main/java/com/ning/billing/catalog/StandaloneCatalog.java
+++ b/catalog/src/main/java/com/ning/billing/catalog/StandaloneCatalog.java
@@ -17,8 +17,10 @@
 package com.ning.billing.catalog;
 
 import java.net.URI;
+import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Date;
+import java.util.List;
 
 import javax.xml.bind.annotation.XmlAccessType;
 import javax.xml.bind.annotation.XmlAccessorType;
@@ -32,6 +34,7 @@ import com.ning.billing.catalog.api.BillingAlignment;
 import com.ning.billing.catalog.api.BillingPeriod;
 import com.ning.billing.catalog.api.CatalogApiException;
 import com.ning.billing.catalog.api.Currency;
+import com.ning.billing.catalog.api.Listing;
 import com.ning.billing.catalog.api.Plan;
 import com.ning.billing.catalog.api.PlanAlignmentChange;
 import com.ning.billing.catalog.api.PlanAlignmentCreate;
@@ -303,15 +306,41 @@ public class StandaloneCatalog extends ValidatingConfig<StandaloneCatalog> imple
         return this;
     }
 
-	@Override
-	public boolean canCreatePlan(PlanSpecifier specifier) throws CatalogApiException {
-		Product product = findCurrentProduct(specifier.getProductName());
-		Plan plan = findCurrentPlan(specifier.getProductName(), specifier.getBillingPeriod(), specifier.getPriceListName());
-		DefaultPriceList priceList = findCurrentPriceList(specifier.getPriceListName());
-		
-		return (!product.isRetired()) &&
-				(!plan.isRetired()) &&
-				(!priceList.isRetired());
-	}
+    @Override
+    public boolean canCreatePlan(PlanSpecifier specifier) throws CatalogApiException {
+        Product product = findCurrentProduct(specifier.getProductName());
+        Plan plan = findCurrentPlan(specifier.getProductName(), specifier.getBillingPeriod(), specifier.getPriceListName());
+        DefaultPriceList priceList = findCurrentPriceList(specifier.getPriceListName());
+        
+        return (!product.isRetired()) &&
+                (!plan.isRetired()) &&
+                (!priceList.isRetired());
+    }
+
+    @Override
+    public List<Listing> getAvailableAddonListings(String baseProductName) {
+        List<Listing> availAddons = new ArrayList<Listing>();
+
+        try {
+            Product product = findCurrentProduct(baseProductName);
+            if ( product != null ) {
+                for ( Product availAddon : product.getAvailable() ) {
+                    for ( Plan plan : getCurrentPlans()) {
+                        if( plan.getProduct().equals(availAddon)) {
+                            for( PriceList priceList : getPriceLists().getAllPriceLists()) {
+                                if ( priceList.findPlan(availAddon, plan.getBillingPeriod()) != null &&
+                                     priceList.findPlan(product, plan.getBillingPeriod()).equals(plan)) {
+                                    availAddons.add(new DefaultListing(plan, priceList));
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+        } catch (CatalogApiException e) {
+            // No such product - just return an empty list
+        }
+
+        return availAddons;    }
 
 }
diff --git a/catalog/src/main/java/com/ning/billing/catalog/VersionedCatalog.java b/catalog/src/main/java/com/ning/billing/catalog/VersionedCatalog.java
index 215b142..3bd370a 100644
--- a/catalog/src/main/java/com/ning/billing/catalog/VersionedCatalog.java
+++ b/catalog/src/main/java/com/ning/billing/catalog/VersionedCatalog.java
@@ -39,6 +39,7 @@ import com.ning.billing.catalog.api.BillingPeriod;
 import com.ning.billing.catalog.api.Catalog;
 import com.ning.billing.catalog.api.CatalogApiException;
 import com.ning.billing.catalog.api.Currency;
+import com.ning.billing.catalog.api.Listing;
 import com.ning.billing.catalog.api.Plan;
 import com.ning.billing.catalog.api.PlanAlignmentChange;
 import com.ning.billing.catalog.api.PlanAlignmentCreate;
@@ -445,4 +446,9 @@ public class VersionedCatalog extends ValidatingConfig<StandaloneCatalog> implem
 		return versionForDate(clock.getUTCNow()).canCreatePlan(specifier);
 	}
 
+    @Override
+    public List<Listing> getAvailableAddonListings(String baseProductName) throws CatalogApiException {
+        return versionForDate(clock.getUTCNow()).getAvailableAddonListings(baseProductName);
+    }
+
 }
diff --git a/catalog/src/test/java/com/ning/billing/catalog/MockCatalog.java b/catalog/src/test/java/com/ning/billing/catalog/MockCatalog.java
index 40dc146..ac090de 100644
--- a/catalog/src/test/java/com/ning/billing/catalog/MockCatalog.java
+++ b/catalog/src/test/java/com/ning/billing/catalog/MockCatalog.java
@@ -184,20 +184,17 @@ public class MockCatalog extends StandaloneCatalog implements Catalog {
 
     @Override
     public ActionPolicy planChangePolicy(PlanPhaseSpecifier from, PlanSpecifier to) throws CatalogApiException {
-        // TODO Auto-generated method stub
         return super.planChangePolicy(from, to);
     }
 
     @Override
     public PlanAlignmentChange planChangeAlignment(PlanPhaseSpecifier from, PlanSpecifier to)
             throws CatalogApiException {
-        // TODO Auto-generated method stub
         return super.planChangeAlignment(from, to);
     }
 
     @Override
     public ActionPolicy planCancelPolicy(PlanPhaseSpecifier planPhase) throws CatalogApiException {
-        // TODO Auto-generated method stub
         return super.planCancelPolicy(planPhase);
     }
 
@@ -208,13 +205,11 @@ public class MockCatalog extends StandaloneCatalog implements Catalog {
 
     @Override
     public BillingAlignment billingAlignment(PlanPhaseSpecifier planPhase) throws CatalogApiException {
-        // TODO Auto-generated method stub
         return billingAlignment;
     }
 
     @Override
     public PlanChangeResult planChange(PlanPhaseSpecifier from, PlanSpecifier to) throws CatalogApiException {
-        // TODO Auto-generated method stub
         return planChange;
     }
 
diff --git a/catalog/src/test/java/com/ning/billing/mock/catalog/MockPlan.java b/catalog/src/test/java/com/ning/billing/mock/catalog/MockPlan.java
index 0657fd4..2199eb9 100644
--- a/catalog/src/test/java/com/ning/billing/mock/catalog/MockPlan.java
+++ b/catalog/src/test/java/com/ning/billing/mock/catalog/MockPlan.java
@@ -46,57 +46,48 @@ public class MockPlan implements Plan {
 
 	@Override
 	public boolean isRetired() {
-		// TODO Auto-generated method stub
-		return false;
+				return false;
 	}
 
 	@Override
 	public Iterator<PlanPhase> getInitialPhaseIterator() {
-		// TODO Auto-generated method stub
-		return null;
+				return null;
 	}
 
 	@Override
 	public PlanPhase getFinalPhase() {
-		// TODO Auto-generated method stub
-		return null;
+				return null;
 	}
 
 	@Override
 	public BillingPeriod getBillingPeriod() {
-		// TODO Auto-generated method stub
-		return null;
+				return null;
 	}
 
 	@Override
 	public int getPlansAllowedInBundle() {
-		// TODO Auto-generated method stub
-		return 0;
+				return 0;
 	}
 
 	@Override
 	public PlanPhase[] getAllPhases() {
-		// TODO Auto-generated method stub
-		return null;
+				return null;
 	}
 
 	@Override
 	public Date getEffectiveDateForExistingSubscriptons() {
-		// TODO Auto-generated method stub
-		return null;
+				return null;
 	}
 
 	@Override
 	public PlanPhase findPhase(String name) throws CatalogApiException {
-		// TODO Auto-generated method stub
-		return null;
+				return null;
 	}
 
 	@Override
 	public DateTime dateOfFirstRecurringNonZeroCharge(
 			DateTime subscriptionStartDate) {
-		// TODO Auto-generated method stub
-		return null;
+				return null;
 	}
 
 }
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiAddOn.java b/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiAddOn.java
index 7990084..9aede3c 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiAddOn.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiAddOn.java
@@ -324,7 +324,6 @@ public class TestUserApiAddOn extends TestApiBase {
         }
     }
 
-    //TODO MDW - debugging reenable if you find this
     @Test(enabled=true, groups={"slow"})
     public void testAddonCreateWithSubscriptionAlign() {
 
diff --git a/invoice/src/main/java/com/ning/billing/invoice/api/invoice/DefaultInvoicePaymentApi.java b/invoice/src/main/java/com/ning/billing/invoice/api/invoice/DefaultInvoicePaymentApi.java
index 11712d2..2283a39 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/api/invoice/DefaultInvoicePaymentApi.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/api/invoice/DefaultInvoicePaymentApi.java
@@ -21,6 +21,7 @@ import java.math.BigDecimal;
 import java.util.List;
 import java.util.UUID;
 
+import com.ning.billing.invoice.api.InvoiceApiException;
 import com.ning.billing.util.callcontext.CallContext;
 import org.joda.time.DateTime;
 
@@ -78,5 +79,41 @@ public class DefaultInvoicePaymentApi implements InvoicePaymentApi {
         dao.notifyOfPaymentAttempt(invoicePayment, context);
     }
 
+    @Override
+    public void processChargeback(UUID invoicePaymentId, BigDecimal amount, CallContext context) throws InvoiceApiException {
+        dao.postChargeback(invoicePaymentId, amount, context);
+    }
+
+    @Override
+    public void processChargeback(UUID invoicePaymentId, CallContext context) throws InvoiceApiException {
+        // use the invoicePaymentId to get the amount remaining on the payment
+        // (preventing charge backs totalling more than the payment)
+        BigDecimal amount = dao.getRemainingAmountPaid(invoicePaymentId);
+        processChargeback(invoicePaymentId, amount, context);
+    }
 
+    @Override
+    public BigDecimal getRemainingAmountPaid(UUID invoicePaymentId) {
+        return dao.getRemainingAmountPaid(invoicePaymentId);
+    }
+
+    @Override
+    public List<InvoicePayment> getChargebacksByAccountId(UUID accountId) {
+        return dao.getChargebacksByAccountId(accountId);
+    }
+
+    @Override
+    public List<InvoicePayment> getChargebacksByPaymentAttemptId(UUID paymentAttemptId) {
+        return dao.getChargebacksByPaymentAttemptId(paymentAttemptId);
+    }
+
+    @Override
+    public InvoicePayment getChargebackById(UUID chargebackId) throws InvoiceApiException {
+        return dao.getChargebackById(chargebackId);
+    }
+
+    @Override
+    public UUID getAccountIdFromInvoicePaymentId(UUID invoicePaymentId) throws InvoiceApiException {
+        return dao.getAccountIdFromInvoicePaymentId(invoicePaymentId);
+    }
 }
\ No newline at end of file
diff --git a/invoice/src/main/java/com/ning/billing/invoice/api/user/DefaultInvoiceUserApi.java b/invoice/src/main/java/com/ning/billing/invoice/api/user/DefaultInvoiceUserApi.java
index 1e45646..7f2ced3 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/api/user/DefaultInvoiceUserApi.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/api/user/DefaultInvoiceUserApi.java
@@ -20,6 +20,8 @@ import java.math.BigDecimal;
 import java.util.List;
 import java.util.UUID;
 
+import com.ning.billing.catalog.api.Currency;
+import com.ning.billing.invoice.api.InvoiceItem;
 import com.ning.billing.util.callcontext.CallContext;
 import org.joda.time.DateTime;
 
@@ -85,7 +87,18 @@ public class DefaultInvoiceUserApi implements InvoiceUserApi {
     }
 
     @Override
-    public void tagInvoiceAsNotWrittenOff(final UUID invoiceId, final CallContext context) {
+    public void tagInvoiceAsNotWrittenOff(final UUID invoiceId, final CallContext context) throws InvoiceApiException {
         dao.removeWrittenOff(invoiceId, context);
     }
+
+    @Override
+    public InvoiceItem getCreditById(final UUID creditId) throws InvoiceApiException {
+        return dao.getCreditById(creditId);
+    }
+
+    @Override
+    public InvoiceItem insertCredit(final UUID accountId, final BigDecimal amount, final DateTime effectiveDate,
+                             final Currency currency, final CallContext context) throws InvoiceApiException {
+        return dao.insertCredit(accountId, amount, effectiveDate, currency, context);
+    }
 }
diff --git a/invoice/src/main/java/com/ning/billing/invoice/dao/CreditInvoiceItemSqlDao.java b/invoice/src/main/java/com/ning/billing/invoice/dao/CreditInvoiceItemSqlDao.java
new file mode 100644
index 0000000..d60e813
--- /dev/null
+++ b/invoice/src/main/java/com/ning/billing/invoice/dao/CreditInvoiceItemSqlDao.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright 2010-2011 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.
+ */
+
+package com.ning.billing.invoice.dao;
+
+import com.ning.billing.catalog.api.Currency;
+import com.ning.billing.invoice.api.InvoiceItem;
+import com.ning.billing.invoice.model.CreditInvoiceItem;
+import com.ning.billing.util.callcontext.CallContext;
+import com.ning.billing.util.callcontext.CallContextBinder;
+import com.ning.billing.util.entity.dao.EntitySqlDao;
+import org.joda.time.DateTime;
+import org.skife.jdbi.v2.SQLStatement;
+import org.skife.jdbi.v2.StatementContext;
+import org.skife.jdbi.v2.sqlobject.Bind;
+import org.skife.jdbi.v2.sqlobject.Binder;
+import org.skife.jdbi.v2.sqlobject.BinderFactory;
+import org.skife.jdbi.v2.sqlobject.BindingAnnotation;
+import org.skife.jdbi.v2.sqlobject.SqlBatch;
+import org.skife.jdbi.v2.sqlobject.SqlQuery;
+import org.skife.jdbi.v2.sqlobject.SqlUpdate;
+import org.skife.jdbi.v2.sqlobject.customizers.RegisterMapper;
+import org.skife.jdbi.v2.sqlobject.stringtemplate.ExternalizedSqlViaStringTemplate3;
+import org.skife.jdbi.v2.tweak.ResultSetMapper;
+
+import java.lang.annotation.Annotation;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import java.math.BigDecimal;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.List;
+import java.util.UUID;
+
+@ExternalizedSqlViaStringTemplate3
+@RegisterMapper(CreditInvoiceItemSqlDao.CreditInvoiceItemMapper.class)
+public interface CreditInvoiceItemSqlDao extends EntitySqlDao<InvoiceItem> {
+    @SqlQuery
+    List<Long> getRecordIds(@Bind("invoiceId") final String invoiceId);
+
+    @SqlQuery
+    List<InvoiceItem> getInvoiceItemsByInvoice(@Bind("invoiceId") final String invoiceId);
+
+    @SqlQuery
+    List<InvoiceItem> getInvoiceItemsByAccount(@Bind("accountId") final String accountId);
+
+    @Override
+    @SqlUpdate
+    void create(@CreditInvoiceItemBinder final InvoiceItem invoiceItem, @CallContextBinder final CallContext context);
+
+    @SqlBatch
+    void create(@CreditInvoiceItemBinder final List<InvoiceItem> items, @CallContextBinder final CallContext context);
+
+    @SqlBatch(transactional=false)
+    void batchCreateFromTransaction(@CreditInvoiceItemBinder final List<InvoiceItem> items, @CallContextBinder final CallContext context);
+
+    @BindingAnnotation(CreditInvoiceItemBinder.CreditInvoiceItemBinderFactory.class)
+    @Retention(RetentionPolicy.RUNTIME)
+    @Target({ElementType.PARAMETER})
+    public @interface CreditInvoiceItemBinder {
+        public static class CreditInvoiceItemBinderFactory implements BinderFactory {
+            public Binder build(Annotation annotation) {
+                return new Binder<CreditInvoiceItemBinder, CreditInvoiceItem>() {
+                    public void bind(SQLStatement q, CreditInvoiceItemBinder bind, CreditInvoiceItem item) {
+                        q.bind("id", item.getId().toString());
+                        q.bind("invoiceId", item.getInvoiceId().toString());
+                        q.bind("accountId", item.getAccountId().toString());
+                        q.bind("creditDate", item.getStartDate().toDate());
+                        q.bind("amount", item.getAmount());
+                        q.bind("currency", item.getCurrency().toString());
+                    }
+                };
+            }
+        }
+    }
+    public class CreditInvoiceItemMapper implements ResultSetMapper<InvoiceItem> {
+        @Override
+        public InvoiceItem map(int index, ResultSet result, StatementContext ctx) throws SQLException {
+            UUID id = UUID.fromString(result.getString("id"));
+            UUID invoiceId = UUID.fromString(result.getString("invoice_id"));
+            UUID accountId = UUID.fromString(result.getString("account_id"));
+            DateTime creditDate = new DateTime(result.getTimestamp("credit_date"));
+            BigDecimal amount = result.getBigDecimal("amount");
+            Currency currency = Currency.valueOf(result.getString("currency"));
+            return new CreditInvoiceItem(id, invoiceId, accountId, creditDate, amount, currency);
+        }
+    }
+}
diff --git a/invoice/src/main/java/com/ning/billing/invoice/dao/DefaultInvoiceDao.java b/invoice/src/main/java/com/ning/billing/invoice/dao/DefaultInvoiceDao.java
index 4b262db..779a359 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/dao/DefaultInvoiceDao.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/dao/DefaultInvoiceDao.java
@@ -21,6 +21,11 @@ import java.util.ArrayList;
 import java.util.List;
 import java.util.UUID;
 
+import com.ning.billing.ErrorCode;
+import com.ning.billing.catalog.api.Currency;
+import com.ning.billing.invoice.api.InvoiceApiException;
+import com.ning.billing.invoice.model.CreditInvoiceItem;
+import com.ning.billing.invoice.model.DefaultInvoice;
 import org.joda.time.DateTime;
 import org.skife.jdbi.v2.IDBI;
 import org.skife.jdbi.v2.Transaction;
@@ -44,6 +49,7 @@ import com.ning.billing.util.tag.dao.TagDao;
 public class DefaultInvoiceDao implements InvoiceDao {
     private final InvoiceSqlDao invoiceSqlDao;
     private final InvoicePaymentSqlDao invoicePaymentSqlDao;
+    private final CreditInvoiceItemSqlDao creditInvoiceItemSqlDao;
     private final TagDao tagDao;
 
 	private final NextBillingDatePoster nextBillingDatePoster;
@@ -54,6 +60,7 @@ public class DefaultInvoiceDao implements InvoiceDao {
                              final TagDao tagDao) {
         this.invoiceSqlDao = dbi.onDemand(InvoiceSqlDao.class);
         this.invoicePaymentSqlDao = dbi.onDemand(InvoicePaymentSqlDao.class);
+        this.creditInvoiceItemSqlDao = dbi.onDemand(CreditInvoiceItemSqlDao.class);
         this.nextBillingDatePoster = nextBillingDatePoster;
         this.tagDao = tagDao;
     }
@@ -117,16 +124,16 @@ public class DefaultInvoiceDao implements InvoiceDao {
     @Override
     public Invoice getById(final UUID invoiceId) {
         return invoiceSqlDao.inTransaction(new Transaction<Invoice, InvoiceSqlDao>() {
-             @Override
-             public Invoice inTransaction(final InvoiceSqlDao invoiceDao, final TransactionStatus status) throws Exception {
-                 Invoice invoice = invoiceDao.getById(invoiceId.toString());
+            @Override
+            public Invoice inTransaction(final InvoiceSqlDao invoiceDao, final TransactionStatus status) throws Exception {
+                Invoice invoice = invoiceDao.getById(invoiceId.toString());
 
-                 if (invoice != null) {
-                     populateChildren(invoice, invoiceDao);
-                 }
+                if (invoice != null) {
+                    populateChildren(invoice, invoiceDao);
+                }
 
-                 return invoice;
-             }
+                return invoice;
+            }
         });
     }
 
@@ -163,6 +170,12 @@ public class DefaultInvoiceDao implements InvoiceDao {
                     recordIdList = fixedPriceInvoiceItemDao.getRecordIds(invoice.getId().toString());
                     audits.addAll(createAudits(TableName.FIXED_INVOICE_ITEMS, recordIdList));
 
+                    List<InvoiceItem> creditInvoiceItems = invoice.getInvoiceItems(CreditInvoiceItem.class);
+                    CreditInvoiceItemSqlDao creditInvoiceItemSqlDao = transactional.become(CreditInvoiceItemSqlDao.class);
+                    creditInvoiceItemSqlDao.batchCreateFromTransaction(creditInvoiceItems, context);
+                    recordIdList = creditInvoiceItemSqlDao.getRecordIds(invoice.getId().toString());
+                    audits.addAll(createAudits(TableName.CREDIT_INVOICE_ITEMS, recordIdList));
+
                     List<InvoicePayment> invoicePayments = invoice.getPayments();
                     InvoicePaymentSqlDao invoicePaymentSqlDao = transactional.become(InvoicePaymentSqlDao.class);
                     invoicePaymentSqlDao.batchCreateFromTransaction(invoicePayments, context);
@@ -177,7 +190,7 @@ public class DefaultInvoiceDao implements InvoiceDao {
         });
     }
 
-    private List<EntityAudit> createAudits(TableName tableName, List<Long> recordIdList) {
+    private List<EntityAudit> createAudits(final TableName tableName, final List<Long> recordIdList) {
         List<EntityAudit> entityAuditList = new ArrayList<EntityAudit>();
         for (Long recordId : recordIdList) {
             entityAuditList.add(new EntityAudit(tableName, recordId, ChangeType.INSERT));
@@ -237,26 +250,96 @@ public class DefaultInvoiceDao implements InvoiceDao {
     }
 
     @Override
-    public UUID getInvoiceIdByPaymentAttemptId(UUID paymentAttemptId) {
+    public UUID getInvoiceIdByPaymentAttemptId(final UUID paymentAttemptId) {
         return invoiceSqlDao.getInvoiceIdByPaymentAttemptId(paymentAttemptId.toString());
     }
 
     @Override
-    public InvoicePayment getInvoicePayment(UUID paymentAttemptId) {
+    public InvoicePayment getInvoicePayment(final UUID paymentAttemptId) {
         return invoicePaymentSqlDao.getInvoicePayment(paymentAttemptId);
     }
 
     @Override
-    public void setWrittenOff(UUID invoiceId, CallContext context) {
+    public void setWrittenOff(final UUID invoiceId, final CallContext context) {
         tagDao.insertTag(invoiceId, ObjectType.INVOICE, ControlTagType.WRITTEN_OFF.toTagDefinition(), context);
     }
 
     @Override
-    public void removeWrittenOff(UUID invoiceId, CallContext context) {
+    public void removeWrittenOff(final UUID invoiceId, final CallContext context) throws InvoiceApiException {
         tagDao.deleteTag(invoiceId, ObjectType.INVOICE, ControlTagType.WRITTEN_OFF.toTagDefinition(), context);
     }
 
     @Override
+    public void postChargeback(final UUID invoicePaymentId, final BigDecimal amount, final CallContext context) throws InvoiceApiException {
+        InvoicePayment payment = invoicePaymentSqlDao.getById(invoicePaymentId.toString());
+        if (payment == null) {
+            throw new InvoiceApiException(ErrorCode.INVOICE_PAYMENT_NOT_FOUND, invoicePaymentId.toString());
+        } else {
+            if (amount.compareTo(BigDecimal.ZERO) < 0) {
+                throw new InvoiceApiException(ErrorCode.CHARGE_BACK_AMOUNT_IS_NEGATIVE);
+            }
+
+            InvoicePayment chargeBack = payment.asChargeBack(amount, context.getCreatedDate());
+            invoicePaymentSqlDao.create(chargeBack, context);
+        }
+    }
+
+    @Override
+    public BigDecimal getRemainingAmountPaid(UUID invoicePaymentId) {
+        BigDecimal amount = invoicePaymentSqlDao.getRemainingAmountPaid(invoicePaymentId.toString());
+        return amount == null ? BigDecimal.ZERO : amount;
+    }
+
+    @Override
+    public UUID getAccountIdFromInvoicePaymentId(UUID invoicePaymentId) throws InvoiceApiException {
+        UUID accountId = invoicePaymentSqlDao.getAccountIdFromInvoicePaymentId(invoicePaymentId.toString());
+        if (accountId == null) {
+            throw new InvoiceApiException(ErrorCode.CHARGE_BACK_COULD_NOT_FIND_ACCOUNT_ID, invoicePaymentId);
+        } else {
+            return accountId;
+        }
+    }
+
+    @Override
+    public List<InvoicePayment> getChargebacksByAccountId(UUID accountId) {
+        return invoicePaymentSqlDao.getChargeBacksByAccountId(accountId.toString());
+    }
+
+    @Override
+    public List<InvoicePayment> getChargebacksByPaymentAttemptId(final UUID paymentAttemptId) {
+        return invoicePaymentSqlDao.getChargebacksByAttemptPaymentId(paymentAttemptId.toString());
+    }
+
+    @Override
+    public InvoicePayment getChargebackById(UUID chargebackId) throws InvoiceApiException {
+        InvoicePayment chargeback = invoicePaymentSqlDao.getById(chargebackId.toString());
+        if (chargeback == null) {
+            throw new InvoiceApiException(ErrorCode.CHARGE_BACK_DOES_NOT_EXIST, chargebackId);
+        } else {
+            return chargeback;
+        }
+    }
+
+    @Override
+    public InvoiceItem getCreditById(final UUID creditId) throws InvoiceApiException {
+        return creditInvoiceItemSqlDao.getById(creditId.toString());
+    }
+
+    // TODO: make this transactional
+    @Override
+    public InvoiceItem insertCredit(final UUID accountId, final BigDecimal amount,
+                             final DateTime effectiveDate, final Currency currency,
+                             final CallContext context) {
+        Invoice invoice = new DefaultInvoice(accountId, effectiveDate, effectiveDate, currency);
+        invoiceSqlDao.create(invoice, context);
+
+        InvoiceItem credit = new CreditInvoiceItem(invoice.getId(), accountId, effectiveDate, amount, currency);
+        creditInvoiceItemSqlDao.create(credit, context);
+
+        return credit;
+    }
+
+    @Override
     public void test() {
         invoiceSqlDao.test();
     }
@@ -278,13 +361,19 @@ public class DefaultInvoiceDao implements InvoiceDao {
     }
 
     private void getInvoiceItemsWithinTransaction(final Invoice invoice, final InvoiceSqlDao invoiceDao) {
+        String invoiceId = invoice.getId().toString();
+
         RecurringInvoiceItemSqlDao recurringInvoiceItemDao = invoiceDao.become(RecurringInvoiceItemSqlDao.class);
-        List<InvoiceItem> recurringInvoiceItems = recurringInvoiceItemDao.getInvoiceItemsByInvoice(invoice.getId().toString());
+        List<InvoiceItem> recurringInvoiceItems = recurringInvoiceItemDao.getInvoiceItemsByInvoice(invoiceId);
         invoice.addInvoiceItems(recurringInvoiceItems);
 
         FixedPriceInvoiceItemSqlDao fixedPriceInvoiceItemDao = invoiceDao.become(FixedPriceInvoiceItemSqlDao.class);
-        List<InvoiceItem> fixedPriceInvoiceItems = fixedPriceInvoiceItemDao.getInvoiceItemsByInvoice(invoice.getId().toString());
+        List<InvoiceItem> fixedPriceInvoiceItems = fixedPriceInvoiceItemDao.getInvoiceItemsByInvoice(invoiceId);
         invoice.addInvoiceItems(fixedPriceInvoiceItems);
+
+        CreditInvoiceItemSqlDao creditInvoiceItemSqlDao = invoiceDao.become(CreditInvoiceItemSqlDao.class);
+        List<InvoiceItem> creditInvoiceItems = creditInvoiceItemSqlDao.getInvoiceItemsByInvoice(invoiceId);
+        invoice.addInvoiceItems(creditInvoiceItems);
     }
 
     private void getInvoicePaymentsWithinTransaction(final List<Invoice> invoices, final InvoiceSqlDao invoiceDao) {
diff --git a/invoice/src/main/java/com/ning/billing/invoice/dao/InvoiceDao.java b/invoice/src/main/java/com/ning/billing/invoice/dao/InvoiceDao.java
index 17006d3..c7e520b 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/dao/InvoiceDao.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/dao/InvoiceDao.java
@@ -16,7 +16,10 @@
 
 package com.ning.billing.invoice.dao;
 
+import com.ning.billing.catalog.api.Currency;
 import com.ning.billing.invoice.api.Invoice;
+import com.ning.billing.invoice.api.InvoiceApiException;
+import com.ning.billing.invoice.api.InvoiceItem;
 import com.ning.billing.invoice.api.InvoicePayment;
 import com.ning.billing.util.callcontext.CallContext;
 import org.joda.time.DateTime;
@@ -52,8 +55,25 @@ public interface InvoiceDao {
 
 	List<Invoice> getAllInvoicesByAccount(final UUID accountId);
 
-    void setWrittenOff(UUID invoiceId, CallContext context);
+    void setWrittenOff(final UUID invoiceId, final CallContext context);
 
-    void removeWrittenOff(UUID invoiceId, CallContext context);
+    void removeWrittenOff(final UUID invoiceId, final CallContext context) throws InvoiceApiException;
 
+    void postChargeback(final UUID invoicePaymentId, final BigDecimal amount, final CallContext context) throws InvoiceApiException;
+
+    BigDecimal getRemainingAmountPaid(final UUID invoicePaymentId);
+
+    UUID getAccountIdFromInvoicePaymentId(final UUID invoicePaymentId) throws InvoiceApiException;
+
+    List<InvoicePayment> getChargebacksByAccountId(final UUID accountId);
+
+    List<InvoicePayment> getChargebacksByPaymentAttemptId(final UUID paymentAttemptId);
+
+    InvoicePayment getChargebackById(final UUID chargebackId) throws InvoiceApiException;
+
+    InvoiceItem getCreditById(final UUID creditId) throws InvoiceApiException;
+
+    InvoiceItem insertCredit(final UUID accountId, final BigDecimal amount,
+                             final DateTime effectiveDate, final Currency currency,
+                             final CallContext context);
 }
diff --git a/invoice/src/main/java/com/ning/billing/invoice/dao/InvoicePaymentSqlDao.java b/invoice/src/main/java/com/ning/billing/invoice/dao/InvoicePaymentSqlDao.java
index a11c8e0..69c4c2a 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/dao/InvoicePaymentSqlDao.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/dao/InvoicePaymentSqlDao.java
@@ -28,10 +28,13 @@ import java.util.List;
 import java.util.UUID;
 
 import com.ning.billing.catalog.api.Currency;
+import com.ning.billing.invoice.model.DefaultInvoicePayment;
 import com.ning.billing.util.dao.AuditSqlDao;
 import com.ning.billing.util.callcontext.CallContext;
 import com.ning.billing.util.callcontext.CallContextBinder;
+import com.ning.billing.util.dao.BinderBase;
 import com.ning.billing.util.dao.MapperBase;
+import com.ning.billing.util.dao.UuidMapper;
 import com.ning.billing.util.entity.dao.EntitySqlDao;
 import org.joda.time.DateTime;
 import org.skife.jdbi.v2.SQLStatement;
@@ -81,7 +84,18 @@ public interface InvoicePaymentSqlDao extends EntitySqlDao<InvoicePayment>, Tran
     void notifyOfPaymentAttempt(@InvoicePaymentBinder final InvoicePayment invoicePayment,
                                 @CallContextBinder final CallContext context);
 
+    @SqlQuery
+    BigDecimal getRemainingAmountPaid(@Bind("invoicePaymentId") final String invoicePaymentId);
+
+    @SqlQuery
+    @RegisterMapper(UuidMapper.class)
+    UUID getAccountIdFromInvoicePaymentId(@Bind("invoicePaymentId") final String invoicePaymentId);
 
+    @SqlQuery
+    List<InvoicePayment> getChargeBacksByAccountId(@Bind("accountId") final String accountId);
+
+    @SqlQuery
+    List<InvoicePayment> getChargebacksByAttemptPaymentId(@Bind("paymentAttemptId") final String paymentAttemptId);
 
     public static class InvoicePaymentMapper extends MapperBase implements ResultSetMapper<InvoicePayment> {
         @Override
@@ -93,33 +107,10 @@ public interface InvoicePaymentSqlDao extends EntitySqlDao<InvoicePayment>, Tran
             final BigDecimal amount = result.getBigDecimal("amount");
             final String currencyString = result.getString("currency");
             final Currency currency = (currencyString == null) ? null : Currency.valueOf(currencyString);
+            final UUID reversedInvoicePaymentId = getUUID(result, "reversed_invoice_Payment_id");
 
-            return new InvoicePayment() {
-                @Override
-                public UUID getId() {
-                    return id;
-                }
-                @Override
-                public UUID getPaymentAttemptId() {
-                    return paymentAttemptId;
-                }
-                @Override
-                public UUID getInvoiceId() {
-                    return invoiceId;
-                }
-                @Override
-                public DateTime getPaymentAttemptDate() {
-                    return paymentAttemptDate;
-                }
-                @Override
-                public BigDecimal getAmount() {
-                    return amount;
-                }
-                @Override
-                public Currency getCurrency() {
-                    return currency;
-                }
-            };
+            return new DefaultInvoicePayment(id, paymentAttemptId, invoiceId, paymentAttemptDate,
+                    amount, currency, reversedInvoicePaymentId);
         }
     }
 
@@ -127,7 +118,7 @@ public interface InvoicePaymentSqlDao extends EntitySqlDao<InvoicePayment>, Tran
     @Retention(RetentionPolicy.RUNTIME)
     @Target({ElementType.PARAMETER})
     public @interface InvoicePaymentBinder {
-        public static class InvoicePaymentBinderFactory implements BinderFactory {
+        public static class InvoicePaymentBinderFactory extends BinderBase implements BinderFactory {
             @Override
             public Binder build(Annotation annotation) {
                 return new Binder<InvoicePaymentBinder, InvoicePayment>() {
@@ -135,11 +126,12 @@ public interface InvoicePaymentSqlDao extends EntitySqlDao<InvoicePayment>, Tran
                     public void bind(SQLStatement q, InvoicePaymentBinder bind, InvoicePayment payment) {
                         q.bind("id", payment.getId().toString());
                         q.bind("invoiceId", payment.getInvoiceId().toString());
-                        q.bind("paymentAttemptId", payment.getPaymentAttemptId().toString());
+                        q.bind("paymentAttemptId", uuidToString(payment.getPaymentAttemptId()));
                         q.bind("paymentAttemptDate", payment.getPaymentAttemptDate().toDate());
                         q.bind("amount", payment.getAmount());
                         Currency currency = payment.getCurrency();
                         q.bind("currency", (currency == null) ? null : currency.toString());
+                        q.bind("reversedInvoicePaymentId", uuidToString(payment.getReversedInvoicePaymentId()));
                     }
                 };
             }
diff --git a/invoice/src/main/java/com/ning/billing/invoice/dao/InvoiceSqlDao.java b/invoice/src/main/java/com/ning/billing/invoice/dao/InvoiceSqlDao.java
index af611bc..04b3fce 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/dao/InvoiceSqlDao.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/dao/InvoiceSqlDao.java
@@ -83,8 +83,6 @@ public interface InvoiceSqlDao extends EntitySqlDao<Invoice>, AuditSqlDao, Trans
     @SqlQuery
     List<Invoice> getUnpaidInvoicesByAccountId(@Bind("accountId") final String accountId,
                                                @Bind("upToDate") final Date upToDate);
-    
-    
 
     @BindingAnnotation(InvoiceBinder.InvoiceBinderFactory.class)
     @Retention(RetentionPolicy.RUNTIME)
diff --git a/invoice/src/main/java/com/ning/billing/invoice/InvoiceDispatcher.java b/invoice/src/main/java/com/ning/billing/invoice/InvoiceDispatcher.java
index 32d444d..b982a3f 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/InvoiceDispatcher.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/InvoiceDispatcher.java
@@ -16,11 +16,11 @@
 
 package com.ning.billing.invoice;
 
+import java.util.ArrayList;
 import java.util.Collection;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
-import java.util.SortedSet;
 import java.util.UUID;
 
 import org.joda.time.DateTime;
@@ -37,18 +37,18 @@ import com.ning.billing.entitlement.api.billing.BillingEvent;
 import com.ning.billing.entitlement.api.billing.EntitlementBillingApiException;
 import com.ning.billing.entitlement.api.user.SubscriptionEvent;
 import com.ning.billing.invoice.api.Invoice;
-import com.ning.billing.invoice.api.InvoiceCreationEvent;
-import com.ning.billing.invoice.api.InvoiceNotifier;
 import com.ning.billing.invoice.api.InvoiceApiException;
+import com.ning.billing.invoice.api.InvoiceCreationEvent;
 import com.ning.billing.invoice.api.InvoiceItem;
+import com.ning.billing.invoice.api.InvoiceNotifier;
 import com.ning.billing.invoice.api.user.DefaultEmptyInvoiceEvent;
 import com.ning.billing.invoice.api.user.DefaultInvoiceCreationEvent;
 import com.ning.billing.invoice.dao.InvoiceDao;
-import com.ning.billing.invoice.model.BillingEventSet;
 import com.ning.billing.invoice.model.FixedPriceInvoiceItem;
 import com.ning.billing.invoice.model.InvoiceGenerator;
 import com.ning.billing.invoice.model.RecurringInvoiceItem;
 import com.ning.billing.junction.api.BillingApi;
+import com.ning.billing.junction.api.BillingEventSet;
 import com.ning.billing.util.bus.Bus;
 import com.ning.billing.util.bus.Bus.EventBusException;
 import com.ning.billing.util.bus.BusEvent;
@@ -145,17 +145,21 @@ public class InvoiceDispatcher {
             final boolean dryRun, final CallContext context) throws InvoiceApiException {
         try {
             Account account = accountUserApi.getAccountById(accountId);
-            SortedSet<BillingEvent> events = billingApi.getBillingEventsForAccountAndUpdateAccountBCD(accountId);
-            BillingEventSet billingEvents = new BillingEventSet(events);
+            BillingEventSet billingEvents = billingApi.getBillingEventsForAccountAndUpdateAccountBCD(accountId);
+            
+            List<Invoice> invoices = new ArrayList<Invoice>();
+            if (!billingEvents.isAccountAutoInvoiceOff()) {
+                invoices = invoiceDao.getInvoicesByAccount(accountId); //no need to fetch, invoicing is off on this account
+            } 
 
             Currency targetCurrency = account.getCurrency();
 
-            List<Invoice> invoices = invoiceDao.getInvoicesByAccount(accountId);
+            
             Invoice invoice = generator.generateInvoice(accountId, billingEvents, invoices, targetDate, targetCurrency);
 
             if (invoice == null) {
                 log.info("Generated null invoice.");
-                outputDebugData(events, invoices);
+                outputDebugData(billingEvents, invoices);
                 if (!dryRun) {
                     BusEvent event = new DefaultEmptyInvoiceEvent(accountId, clock.getUTCNow(), context.getUserToken());
                     postEvent(event, accountId);
@@ -168,7 +172,7 @@ public class InvoiceDispatcher {
                         log.info(item.toString());
                     }
                 }
-                outputDebugData(events, invoices);
+                outputDebugData(billingEvents, invoices);
                 if (!dryRun) {
                     invoiceDao.create(invoice, context);
 
diff --git a/invoice/src/main/java/com/ning/billing/invoice/model/CreditInvoiceItem.java b/invoice/src/main/java/com/ning/billing/invoice/model/CreditInvoiceItem.java
index ac800a7..651a717 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/model/CreditInvoiceItem.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/model/CreditInvoiceItem.java
@@ -18,6 +18,7 @@ package com.ning.billing.invoice.model;
 
 import com.ning.billing.catalog.api.Currency;
 import com.ning.billing.invoice.api.InvoiceItem;
+import com.ning.billing.invoice.api.InvoiceItemType;
 import org.joda.time.DateTime;
 
 import java.math.BigDecimal;
@@ -29,7 +30,7 @@ public class CreditInvoiceItem extends InvoiceItemBase {
     }
 
     public CreditInvoiceItem(UUID id, UUID invoiceId, UUID accountId, DateTime date, BigDecimal amount, Currency currency) {
-        super(id, invoiceId, accountId, null, null, null, null, date, date, amount, currency);
+        super(id, invoiceId, accountId, null, null, null, null, date, date, amount, currency, InvoiceItemType.CREDIT);
     }
 
     @Override
diff --git a/invoice/src/main/java/com/ning/billing/invoice/model/DefaultInvoice.java b/invoice/src/main/java/com/ning/billing/invoice/model/DefaultInvoice.java
index 0de9aa4..952ff60 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/model/DefaultInvoice.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/model/DefaultInvoice.java
@@ -173,18 +173,24 @@ public class DefaultInvoice extends EntityBase implements Invoice {
     }
 
     @Override
-    public BigDecimal getTotalAmount() {
-        return invoiceItems.getTotalAmount();
+    public BigDecimal getAmountCharged() {
+        return invoiceItems.getAmountCharged();
+    }
+
+    @Override
+    public BigDecimal getAmountCredited() {
+        return invoiceItems.getAmountCredited();
     }
 
     @Override
     public BigDecimal getBalance() {
-        return getTotalAmount().subtract(getAmountPaid());
+        // credits offset payments
+        return getAmountCharged().subtract(getAmountPaid().subtract(getAmountCredited()));
     }
 
     @Override
     public boolean isDueForPayment(final DateTime targetDate, final int numberOfDays) {
-        if (getTotalAmount().compareTo(BigDecimal.ZERO) == 0) {
+        if (getBalance().compareTo(BigDecimal.ZERO) == 0) {
             return false;
         }
 
diff --git a/invoice/src/main/java/com/ning/billing/invoice/model/DefaultInvoiceGenerator.java b/invoice/src/main/java/com/ning/billing/invoice/model/DefaultInvoiceGenerator.java
index c24215c..08e19c0 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/model/DefaultInvoiceGenerator.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/model/DefaultInvoiceGenerator.java
@@ -41,6 +41,7 @@ import com.ning.billing.entitlement.api.billing.BillingModeType;
 import com.ning.billing.invoice.api.Invoice;
 import com.ning.billing.invoice.api.InvoiceApiException;
 import com.ning.billing.invoice.api.InvoiceItem;
+import com.ning.billing.junction.api.BillingEventSet;
 import com.ning.billing.util.clock.Clock;
 
 public class DefaultInvoiceGenerator implements InvoiceGenerator {
@@ -64,18 +65,24 @@ public class DefaultInvoiceGenerator implements InvoiceGenerator {
                                          @Nullable final List<Invoice> existingInvoices,
                                          DateTime targetDate,
                                          final Currency targetCurrency) throws InvoiceApiException {
-        if ((events == null) || (events.size() == 0)) {
+        if ((events == null) || (events.size() == 0) || events.isAccountAutoInvoiceOff()) {
             return null;
         }
 
         validateTargetDate(targetDate);
-
-        Collections.sort(events);
+        //TODO MDW can use subscription Id - not bundle
+        //TODO MDW worry about null sub id
 
         List<InvoiceItem> existingItems = new ArrayList<InvoiceItem>();
         if (existingInvoices != null) {
             for (Invoice invoice : existingInvoices) {
-                existingItems.addAll(invoice.getInvoiceItems());
+                for(InvoiceItem item : invoice.getInvoiceItems()) {
+                    if(item.getSubscriptionId() == null || // Always include migration invoices, credits etc.  
+                      !events.getSubscriptionIdsWithAutoInvoiceOff()
+                            .contains(item.getSubscriptionId())) { //don't add items with auto_invoice_off tag 
+                        existingItems.add(item);
+                    }
+                }
             }
 
             Collections.sort(existingItems);
@@ -83,21 +90,16 @@ public class DefaultInvoiceGenerator implements InvoiceGenerator {
 
         targetDate = adjustTargetDate(existingInvoices, targetDate);
 
-        DefaultInvoice invoice = new DefaultInvoice(accountId, clock.getUTCNow(), targetDate, targetCurrency);
+        Invoice invoice = new DefaultInvoice(accountId, clock.getUTCNow(), targetDate, targetCurrency);
         UUID invoiceId = invoice.getId();
         List<InvoiceItem> proposedItems = generateInvoiceItems(invoiceId, accountId, events, targetDate, targetCurrency);
 
         removeCancellingInvoiceItems(existingItems);
         removeDuplicatedInvoiceItems(proposedItems, existingItems);
 
-        for (InvoiceItem existingItem : existingItems) {
-            if (existingItem instanceof RecurringInvoiceItem) {
-                RecurringInvoiceItem recurringItem = (RecurringInvoiceItem) existingItem;
-                proposedItems.add(recurringItem.asReversingItem());
-            }
-        }
-
-        //addCreditItems(accountId, proposedItems, existingInvoices, targetCurrency);
+        addReversingItems(existingItems, proposedItems);
+        generateCreditsForPastRepairedInvoices(accountId, existingInvoices, proposedItems, targetCurrency);
+        consumeExistingCredit(invoiceId, accountId, existingItems, proposedItems, targetCurrency);
 
         if (proposedItems == null || proposedItems.size() == 0) {
             return null;
@@ -108,75 +110,75 @@ public class DefaultInvoiceGenerator implements InvoiceGenerator {
         }
     }
 
-   /*
-    * ensures that the balance of all invoices are zero or positive, adding an adjusting credit item if needed
-    */
-    private void addCreditItems(UUID accountId, List<InvoiceItem> invoiceItems, List<Invoice> invoices, Currency currency) {
-        Map<UUID, BigDecimal> invoiceBalances = new HashMap<UUID, BigDecimal>();
-
-        updateInvoiceBalance(invoiceItems, invoiceBalances);
+    private void generateCreditsForPastRepairedInvoices(UUID accountId, List<Invoice> existingInvoices, List<InvoiceItem> proposedItems, Currency currency) {
+        // determine most accurate invoice balances up to this point
+        Map<UUID, BigDecimal> amountOwedByInvoice = new HashMap<UUID, BigDecimal>();
 
-        // add all existing items and payments
-        if (invoices != null) {
-            for (Invoice invoice : invoices) {
-                updateInvoiceBalance(invoice.getInvoiceItems(), invoiceBalances);
-            }
-
-            for (Invoice invoice : invoices) {
-                UUID invoiceId = invoice.getId();
-                invoiceBalances.put(invoiceId, invoiceBalances.get(invoiceId).subtract(invoice.getAmountPaid()));
+        if (existingInvoices != null) {
+            for (Invoice invoice : existingInvoices) {
+                amountOwedByInvoice.put(invoice.getId(), invoice.getBalance());
             }
         }
 
-        BigDecimal creditTotal = BigDecimal.ZERO;
-
-        for (UUID invoiceId : invoiceBalances.keySet()) {
-            BigDecimal balance = invoiceBalances.get(invoiceId);
-            if (balance.compareTo(BigDecimal.ZERO) < 0) {
-                creditTotal = creditTotal.add(balance.negate());
-                invoiceItems.add(new CreditInvoiceItem(invoiceId, accountId, clock.getUTCNow(), balance, currency));
+        for (InvoiceItem item : proposedItems) {
+            UUID invoiceId = item.getInvoiceId();
+            if (amountOwedByInvoice.containsKey(invoiceId)) {
+                amountOwedByInvoice.put(invoiceId, amountOwedByInvoice.get(invoiceId).add(item.getAmount()));
+            } else {
+                amountOwedByInvoice.put(invoiceId, item.getAmount());
             }
         }
 
-        if (creditTotal.compareTo(BigDecimal.ZERO) != 0) {
-            // create a single credit item to cover all credits
-            //invoiceItems.add(new CreditInvoiceItem());
+        for (UUID invoiceId : amountOwedByInvoice.keySet()) {
+            BigDecimal invoiceBalance = amountOwedByInvoice.get(invoiceId);
+            if (invoiceBalance.compareTo(BigDecimal.ZERO) < 0) {
+                proposedItems.add(new CreditInvoiceItem(invoiceId, accountId, clock.getUTCNow(), invoiceBalance.negate(), currency));
+            }
         }
     }
 
-    private void updateInvoiceBalance(List<InvoiceItem> items, Map<UUID, BigDecimal> invoiceBalances) {
-        for (InvoiceItem item : items) {
-            UUID invoiceId = item.getInvoiceId();
-
-            if (!invoiceBalances.containsKey(invoiceId)) {
-                invoiceBalances.put(invoiceId, BigDecimal.ZERO);
+    private void addReversingItems(List<InvoiceItem> existingItems, List<InvoiceItem> proposedItems) {
+        for (InvoiceItem existingItem : existingItems) {
+            if (existingItem instanceof RecurringInvoiceItem) {
+                RecurringInvoiceItem recurringItem = (RecurringInvoiceItem) existingItem;
+                proposedItems.add(recurringItem.asReversingItem());
             }
-
-            invoiceBalances.put(invoiceId, invoiceBalances.get(invoiceId).add(item.getAmount()));
         }
     }
 
-    @Override
-    public void distributeItems(List<Invoice> invoices) {
-        Map<UUID, Invoice> invoiceMap = new HashMap<UUID, Invoice>();
+    private void consumeExistingCredit(UUID invoiceId, UUID accountId, List<InvoiceItem> existingItems,
+                                       List<InvoiceItem> proposedItems, Currency targetCurrency) {
+        BigDecimal totalUnusedCreditAmount = BigDecimal.ZERO;
+        BigDecimal totalAmountOwed = BigDecimal.ZERO;
 
-        for (Invoice invoice : invoices) {
-            invoiceMap.put(invoice.getId(), invoice);
+        for (InvoiceItem item : existingItems) {
+            if (item instanceof CreditInvoiceItem) {
+                totalUnusedCreditAmount = totalUnusedCreditAmount.add(item.getAmount());
+            }
         }
 
-        for (final Invoice invoice: invoices) {
-            Iterator<InvoiceItem> itemIterator = invoice.getInvoiceItems().iterator();
-            final UUID invoiceId = invoice.getId();
-
-            while (itemIterator.hasNext()) {
-                InvoiceItem item = itemIterator.next();
+        for (InvoiceItem item : proposedItems) {
+            if (item instanceof CreditInvoiceItem) {
+                totalUnusedCreditAmount = totalUnusedCreditAmount.add(item.getAmount());
+            } else {
+                totalAmountOwed = totalAmountOwed.add(item.getAmount());
+            }
+        }
 
-                if (!item.getInvoiceId().equals(invoiceId)) {
-                    invoiceMap.get(item.getInvoiceId()).addInvoiceItem(item);
-                    itemIterator.remove();
-                }
+        // credits are positive when they reduce the amount owed (since they offset payment)
+        // the credit balance should never be negative
+        BigDecimal creditAmount = BigDecimal.ZERO;
+        if (totalUnusedCreditAmount.compareTo(BigDecimal.ZERO) > 0) {
+            if (totalAmountOwed.abs().compareTo(totalUnusedCreditAmount.abs()) > 0) {
+                creditAmount = totalUnusedCreditAmount.negate();
+            } else {
+                creditAmount = totalAmountOwed;
             }
         }
+
+        if (creditAmount.compareTo(BigDecimal.ZERO) < 0) {
+            proposedItems.add(new CreditInvoiceItem(invoiceId, accountId, clock.getUTCNow(), creditAmount, targetCurrency));
+        }
     }
 
     private void validateTargetDate(DateTime targetDate) throws InvoiceApiException {
@@ -246,16 +248,36 @@ public class DefaultInvoiceGenerator implements InvoiceGenerator {
     private List<InvoiceItem> generateInvoiceItems(final UUID invoiceId, final UUID accountId, final BillingEventSet events,
                                                    final DateTime targetDate, final Currency currency) throws InvoiceApiException {
         List<InvoiceItem> items = new ArrayList<InvoiceItem>();
+        
+        if(events.size() == 0) {
+            return items;
+        }
 
-        for (int i = 0; i < events.size(); i++) {
-            BillingEvent thisEvent = events.get(i);
-            BillingEvent nextEvent = events.isLast(thisEvent) ? null : events.get(i + 1);
-            if (nextEvent != null) {
-                nextEvent = (thisEvent.getSubscription().getId() == nextEvent.getSubscription().getId()) ? nextEvent : null;
-            }
+        Iterator<BillingEvent> eventIt = events.iterator();
 
-            items.addAll(processEvents(invoiceId, accountId, thisEvent, nextEvent, targetDate, currency));
+        BillingEvent nextEvent = eventIt.next();
+        while(eventIt.hasNext()) {
+            BillingEvent thisEvent = nextEvent;
+            nextEvent = eventIt.next();
+            if(!events.getSubscriptionIdsWithAutoInvoiceOff().
+                    contains(thisEvent.getSubscription().getId())) { // don't consider events for subscriptions that have auto_invoice_off
+                BillingEvent adjustedNextEvent = (thisEvent.getSubscription().getId() == nextEvent.getSubscription().getId()) ? nextEvent : null;
+                items.addAll(processEvents(invoiceId, accountId, thisEvent, adjustedNextEvent, targetDate, currency));
+            }
         }
+        items.addAll(processEvents(invoiceId, accountId, nextEvent, null, targetDate, currency));
+        
+// The above should reproduce the semantics of the code below using iterator instead of list.
+//
+//        for (int i = 0; i < events.size(); i++) {
+//            BillingEvent thisEvent = events.get(i);
+//            BillingEvent nextEvent = events.isLast(thisEvent) ? null : events.get(i + 1);
+//            if (nextEvent != null) {
+//                nextEvent = (thisEvent.getSubscription().getId() == nextEvent.getSubscription().getId()) ? nextEvent : null;
+//            }
+//
+//            items.addAll(processEvents(invoiceId, accountId, thisEvent, nextEvent, targetDate, currency));
+//        }
 
         return items;
     }
diff --git a/invoice/src/main/java/com/ning/billing/invoice/model/DefaultInvoicePayment.java b/invoice/src/main/java/com/ning/billing/invoice/model/DefaultInvoicePayment.java
index ba02039..1be9662 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/model/DefaultInvoicePayment.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/model/DefaultInvoicePayment.java
@@ -16,7 +16,9 @@
 
 package com.ning.billing.invoice.model;
 
+import com.ning.billing.ErrorCode;
 import com.ning.billing.catalog.api.Currency;
+import com.ning.billing.invoice.api.InvoiceApiException;
 import com.ning.billing.invoice.api.InvoicePayment;
 import com.ning.billing.util.entity.EntityBase;
 import org.joda.time.DateTime;
@@ -31,24 +33,27 @@ public class DefaultInvoicePayment extends EntityBase implements InvoicePayment 
     private final DateTime paymentDate;
     private final BigDecimal amount;
     private final Currency currency;
+    private final UUID reversedInvoicePaymentId;
 
     public DefaultInvoicePayment(final UUID paymentAttemptId, final UUID invoiceId, final DateTime paymentDate) {
-        this(UUID.randomUUID(), paymentAttemptId, invoiceId, paymentDate, null, null);
+        this(UUID.randomUUID(), paymentAttemptId, invoiceId, paymentDate, null, null, null);
     }
 
     public DefaultInvoicePayment(final UUID paymentAttemptId, final UUID invoiceId, final DateTime paymentDate,
                                  final BigDecimal amount, final Currency currency) {
-        this(UUID.randomUUID(), paymentAttemptId, invoiceId, paymentDate, amount, currency);
+        this(UUID.randomUUID(), paymentAttemptId, invoiceId, paymentDate, amount, currency, null);
     }
 
     public DefaultInvoicePayment(final UUID id, final UUID paymentAttemptId, final UUID invoiceId, final DateTime paymentDate,
-                                 @Nullable final BigDecimal amount, @Nullable final Currency currency) {
+                                 @Nullable final BigDecimal amount, @Nullable final Currency currency,
+                                 @Nullable final UUID reversedInvoicePaymentId) {
         super(id);
         this.paymentAttemptId = paymentAttemptId;
         this.amount = amount;
         this.invoiceId = invoiceId;
         this.paymentDate = paymentDate;
         this.currency = currency;
+        this.reversedInvoicePaymentId = reversedInvoicePaymentId;
     }
 
     @Override
@@ -75,4 +80,18 @@ public class DefaultInvoicePayment extends EntityBase implements InvoicePayment 
     public Currency getCurrency() {
         return currency;
     }
+
+    @Override
+    public UUID getReversedInvoicePaymentId() {
+        return reversedInvoicePaymentId;
+    }
+
+    @Override
+    public InvoicePayment asChargeBack(BigDecimal chargeBackAmount, DateTime chargeBackDate) throws InvoiceApiException {
+        if (chargeBackAmount.compareTo(amount) > 0) {
+            throw new InvoiceApiException(ErrorCode.CHARGE_BACK_AMOUNT_TOO_HIGH, chargeBackAmount, amount);
+        }
+
+        return new DefaultInvoicePayment(UUID.randomUUID(), null, invoiceId, chargeBackDate, chargeBackAmount.negate(), currency, id);
+    }
 }
diff --git a/invoice/src/main/java/com/ning/billing/invoice/model/FixedPriceInvoiceItem.java b/invoice/src/main/java/com/ning/billing/invoice/model/FixedPriceInvoiceItem.java
index b5a79ab..60a39a6 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/model/FixedPriceInvoiceItem.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/model/FixedPriceInvoiceItem.java
@@ -19,6 +19,7 @@ package com.ning.billing.invoice.model;
 import java.math.BigDecimal;
 import java.util.UUID;
 
+import com.ning.billing.invoice.api.InvoiceItemType;
 import org.joda.time.DateTime;
 
 import com.ning.billing.catalog.api.Currency;
@@ -28,12 +29,12 @@ public class FixedPriceInvoiceItem extends InvoiceItemBase {
 
     public FixedPriceInvoiceItem(UUID invoiceId, UUID accountId, UUID bundleId, UUID subscriptionId, String planName, String phaseName,
                                  DateTime startDate, DateTime endDate, BigDecimal amount, Currency currency) {
-        super(invoiceId, accountId, bundleId, subscriptionId, planName, phaseName, startDate, endDate, amount, currency);
+        super(invoiceId, accountId, bundleId, subscriptionId, planName, phaseName, startDate, endDate, amount, currency, InvoiceItemType.FIXED);
     }
 
     public FixedPriceInvoiceItem(UUID id, UUID invoiceId, UUID accountId, UUID bundleId, UUID subscriptionId, String planName, String phaseName,
                                  DateTime startDate, DateTime endDate, BigDecimal amount, Currency currency) {
-        super(id, invoiceId, accountId, bundleId, subscriptionId, planName, phaseName, startDate, endDate, amount, currency);
+        super(id, invoiceId, accountId, bundleId, subscriptionId, planName, phaseName, startDate, endDate, amount, currency, InvoiceItemType.FIXED);
     }
 
     @Override
diff --git a/invoice/src/main/java/com/ning/billing/invoice/model/InvoiceGenerator.java b/invoice/src/main/java/com/ning/billing/invoice/model/InvoiceGenerator.java
index f903087..0b47099 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/model/InvoiceGenerator.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/model/InvoiceGenerator.java
@@ -16,19 +16,19 @@
 
 package com.ning.billing.invoice.model;
 
+import java.util.List;
+import java.util.UUID;
+
+import javax.annotation.Nullable;
+
+import org.joda.time.DateTime;
+
 import com.ning.billing.catalog.api.Currency;
 import com.ning.billing.invoice.api.Invoice;
 import com.ning.billing.invoice.api.InvoiceApiException;
-import com.ning.billing.invoice.api.InvoiceItem;
-import org.joda.time.DateTime;
-
-import javax.annotation.Nullable;
-import java.util.List;
-import java.util.UUID;
+import com.ning.billing.junction.api.BillingEventSet;
 
 public interface InvoiceGenerator {
     public Invoice generateInvoice(UUID accountId, @Nullable BillingEventSet events, @Nullable List<Invoice> existingInvoices,
                                    DateTime targetDate, Currency targetCurrency) throws InvoiceApiException;
-
-    public void distributeItems(List<Invoice> invoices);
 }
diff --git a/invoice/src/main/java/com/ning/billing/invoice/model/InvoiceItemBase.java b/invoice/src/main/java/com/ning/billing/invoice/model/InvoiceItemBase.java
index 5ccaa8c..016184d 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/model/InvoiceItemBase.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/model/InvoiceItemBase.java
@@ -18,6 +18,7 @@ package com.ning.billing.invoice.model;
 
 import com.ning.billing.catalog.api.Currency;
 import com.ning.billing.invoice.api.InvoiceItem;
+import com.ning.billing.invoice.api.InvoiceItemType;
 import com.ning.billing.util.entity.EntityBase;
 import org.joda.time.DateTime;
 
@@ -36,16 +37,18 @@ public abstract class InvoiceItemBase extends EntityBase implements InvoiceItem 
     protected final DateTime endDate;
     protected final BigDecimal amount;
     protected final Currency currency;
+    protected InvoiceItemType invoiceItemType;
 
     public InvoiceItemBase(UUID invoiceId, UUID accountId, UUID bundleId, UUID subscriptionId, String planName, String phaseName,
-            DateTime startDate, DateTime endDate, BigDecimal amount, Currency currency) {
+            DateTime startDate, DateTime endDate, BigDecimal amount, Currency currency, InvoiceItemType invoiceItemType) {
         this(UUID.randomUUID(), invoiceId, accountId, bundleId, subscriptionId, planName, phaseName,
-                startDate, endDate, amount, currency);
+                startDate, endDate, amount, currency, invoiceItemType);
     }
 
     public InvoiceItemBase(UUID id, UUID invoiceId, UUID accountId, @Nullable UUID bundleId,
                            @Nullable UUID subscriptionId, String planName, String phaseName,
-                           DateTime startDate, DateTime endDate, BigDecimal amount, Currency currency) {
+                           DateTime startDate, DateTime endDate, BigDecimal amount, Currency currency,
+                           InvoiceItemType invoiceItemType) {
         super(id);
         this.invoiceId = invoiceId;
         this.accountId = accountId;
@@ -57,6 +60,7 @@ public abstract class InvoiceItemBase extends EntityBase implements InvoiceItem 
         this.endDate = endDate;
         this.amount = amount;
         this.currency = currency;
+        this.invoiceItemType = invoiceItemType;
     }
 
     @Override
@@ -109,6 +113,11 @@ public abstract class InvoiceItemBase extends EntityBase implements InvoiceItem 
     }
 
     @Override
+    public InvoiceItemType getInvoiceItemType() {
+        return invoiceItemType;
+    }
+
+    @Override
     public abstract InvoiceItem asReversingItem();
 
     @Override
diff --git a/invoice/src/main/java/com/ning/billing/invoice/model/InvoiceItemList.java b/invoice/src/main/java/com/ning/billing/invoice/model/InvoiceItemList.java
index 683bbaf..357c7a8 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/model/InvoiceItemList.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/model/InvoiceItemList.java
@@ -34,55 +34,33 @@ public class InvoiceItemList extends ArrayList<InvoiceItem> {
         this.addAll(invoiceItems);
     }
 
-    public BigDecimal getTotalAmount() {
+    public BigDecimal getAmountCharged() {
         // naive implementation, assumes all invoice items share the same currency
         BigDecimal total = BigDecimal.ZERO.setScale(NUMBER_OF_DECIMALS, ROUNDING_METHOD);
 
         for (final InvoiceItem item : this) {
-            if (item.getAmount() != null) {
-                total = total.add(item.getAmount());
+            if (!(item instanceof CreditInvoiceItem)) {
+                if (item.getAmount() != null) {
+                    total = total.add(item.getAmount());
+                }
             }
         }
 
         return total.setScale(NUMBER_OF_DECIMALS, ROUNDING_METHOD);
     }
 
-//    public void removeCancellingPairs() {
-//        List<InvoiceItem> itemsToRemove = new ArrayList<InvoiceItem>();
-//
-//        for (int firstItemIndex = 0; firstItemIndex < this.size(); firstItemIndex++) {
-//            for (int secondItemIndex = firstItemIndex + 1; secondItemIndex < this.size(); secondItemIndex++) {
-//                InvoiceItem firstItem = this.get(firstItemIndex);
-//                InvoiceItem secondItem = this.get(secondItemIndex);
-//                if (firstItem.cancels(secondItem)) {
-//                    itemsToRemove.add(firstItem);
-//                    itemsToRemove.add(secondItem);
-//                }
-//            }
-//        }
-//
-//        this.removeAll(itemsToRemove);
-//    }
+    public BigDecimal getAmountCredited() {
+        // naive implementation, assumes all invoice items share the same currency
+        BigDecimal total = BigDecimal.ZERO.setScale(NUMBER_OF_DECIMALS, ROUNDING_METHOD);
+
+        for (final InvoiceItem item : this) {
+            if (item instanceof CreditInvoiceItem) {
+                if (item.getAmount() != null) {
+                    total = total.add(item.getAmount());
+                }
+            }
+        }
 
-//   /*
-//    * removes recurring items from the list that have a recurring amount of zero, but a recurring rate that is not zero
-//    */
-//    public void cleanupDuplicatedItems() {
-//        Iterator<InvoiceItem> iterator = this.iterator();
-//        while (iterator.hasNext()) {
-//            InvoiceItem item = iterator.next();
-//
-//            if (item instanceof RecurringInvoiceItem) {
-//                RecurringInvoiceItem that = (RecurringInvoiceItem) item;
-//                boolean recurringRateNull = (that.getRate() == null);
-//                boolean recurringAmountZero = (that.getAmount() !=null) && (that.getAmount().compareTo(BigDecimal.ZERO) == 0);
-//
-//                if (recurringRateNull || recurringAmountZero) {
-//                    iterator.remove();
-//                } else if (that.getEndDate() != null && that.getStartDate().compareTo(that.getEndDate()) == 0) {
-//                    iterator.remove();
-//                }
-//            }
-//        }
-//    }
+        return total.setScale(NUMBER_OF_DECIMALS, ROUNDING_METHOD);
+    }
 }
diff --git a/invoice/src/main/java/com/ning/billing/invoice/model/RecurringInvoiceItem.java b/invoice/src/main/java/com/ning/billing/invoice/model/RecurringInvoiceItem.java
index bc813fc..9dbbae0 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/model/RecurringInvoiceItem.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/model/RecurringInvoiceItem.java
@@ -18,6 +18,7 @@ package com.ning.billing.invoice.model;
 
 import com.ning.billing.catalog.api.Currency;
 import com.ning.billing.invoice.api.InvoiceItem;
+import com.ning.billing.invoice.api.InvoiceItemType;
 import org.joda.time.DateTime;
 
 import java.math.BigDecimal;
@@ -30,8 +31,8 @@ public class RecurringInvoiceItem extends InvoiceItemBase {
     public RecurringInvoiceItem(UUID invoiceId, UUID accountId, UUID bundleId, UUID subscriptionId, String planName, String phaseName,
                                 DateTime startDate, DateTime endDate,
                                 BigDecimal amount, BigDecimal rate,
-                                Currency currency) { 
-        super(invoiceId, accountId, bundleId, subscriptionId, planName, phaseName, startDate, endDate, amount, currency);
+                                Currency currency) {
+        super(invoiceId, accountId, bundleId, subscriptionId, planName, phaseName, startDate, endDate, amount, currency, InvoiceItemType.RECURRING);
         this.rate = rate;
         this.reversedItemId = null;
     }
@@ -41,7 +42,7 @@ public class RecurringInvoiceItem extends InvoiceItemBase {
                                 BigDecimal amount, BigDecimal rate,
                                 Currency currency, UUID reversedItemId) {
         super(invoiceId, accountId, bundleId, subscriptionId, planName, phaseName, startDate, endDate,
-                amount, currency);
+                amount, currency, InvoiceItemType.REVERSAL);
         this.rate = rate;
         this.reversedItemId = reversedItemId;
     }
@@ -51,7 +52,7 @@ public class RecurringInvoiceItem extends InvoiceItemBase {
                                 DateTime startDate, DateTime endDate,
                                 BigDecimal amount, BigDecimal rate,
                                 Currency currency) {
-        super(id, invoiceId, accountId, bundleId, subscriptionId, planName, phaseName, startDate, endDate, amount, currency);
+        super(id, invoiceId, accountId, bundleId, subscriptionId, planName, phaseName, startDate, endDate, amount, currency, InvoiceItemType.RECURRING);
         this.rate = rate;
         this.reversedItemId = null;
     }
@@ -61,7 +62,7 @@ public class RecurringInvoiceItem extends InvoiceItemBase {
                                 DateTime startDate, DateTime endDate,
                                 BigDecimal amount, BigDecimal rate,
                                 Currency currency, UUID reversedItemId) {
-        super(id, invoiceId, accountId, bundleId, subscriptionId, planName, phaseName, startDate, endDate, amount, currency);
+        super(id, invoiceId, accountId, bundleId, subscriptionId, planName, phaseName, startDate, endDate, amount, currency, InvoiceItemType.REVERSAL);
         this.rate = rate;
         this.reversedItemId = reversedItemId;
     }
@@ -132,6 +133,7 @@ public class RecurringInvoiceItem extends InvoiceItemBase {
 
         RecurringInvoiceItem that = (RecurringInvoiceItem) o;
 
+        // do not include invoice item type, since a reversing item can be equal to the original item
         if (accountId.compareTo(that.accountId) != 0) return false;
         if (amount.compareTo(that.amount) != 0) return false;
         if (currency != that.currency) return false;
@@ -162,6 +164,7 @@ public class RecurringInvoiceItem extends InvoiceItemBase {
         result = 31 * result + amount.hashCode();
         result = 31 * result + rate.hashCode();
         result = 31 * result + currency.hashCode();
+        result = 31 * result + invoiceItemType.hashCode();
         result = 31 * result + (reversedItemId != null ? reversedItemId.hashCode() : 0);
         return result;
     }
diff --git a/invoice/src/main/java/com/ning/billing/invoice/template/formatters/DefaultInvoiceFormatter.java b/invoice/src/main/java/com/ning/billing/invoice/template/formatters/DefaultInvoiceFormatter.java
index ed50d2e..1ca7824 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/template/formatters/DefaultInvoiceFormatter.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/template/formatters/DefaultInvoiceFormatter.java
@@ -120,8 +120,13 @@ public class DefaultInvoiceFormatter implements InvoiceFormatter {
     }
 
     @Override
-    public BigDecimal getTotalAmount() {
-        return invoice.getTotalAmount();
+    public BigDecimal getAmountCharged() {
+        return invoice.getAmountCharged();
+    }
+
+    @Override
+    public BigDecimal getAmountCredited() {
+        return invoice.getAmountCredited();
     }
 
     @Override
diff --git a/invoice/src/main/java/com/ning/billing/invoice/template/formatters/DefaultInvoiceItemFormatter.java b/invoice/src/main/java/com/ning/billing/invoice/template/formatters/DefaultInvoiceItemFormatter.java
index f1a2e8d..9f49eb5 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/template/formatters/DefaultInvoiceItemFormatter.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/template/formatters/DefaultInvoiceItemFormatter.java
@@ -18,6 +18,7 @@ package com.ning.billing.invoice.template.formatters;
 
 import com.ning.billing.catalog.api.Currency;
 import com.ning.billing.invoice.api.InvoiceItem;
+import com.ning.billing.invoice.api.InvoiceItemType;
 import com.ning.billing.invoice.api.formatters.InvoiceItemFormatter;
 import com.ning.billing.util.template.translation.DefaultCatalogTranslator;
 import com.ning.billing.util.template.translation.Translator;
@@ -55,6 +56,11 @@ public class DefaultInvoiceItemFormatter implements InvoiceItemFormatter {
     }
 
     @Override
+    public InvoiceItemType getInvoiceItemType() {
+        return item.getInvoiceItemType();
+    }
+
+    @Override
     public InvoiceItem asReversingItem() {
         return item.asReversingItem();
     }
diff --git a/invoice/src/main/resources/com/ning/billing/invoice/dao/CreditInvoiceItemSqlDao.sql.stg b/invoice/src/main/resources/com/ning/billing/invoice/dao/CreditInvoiceItemSqlDao.sql.stg
new file mode 100644
index 0000000..d3bef75
--- /dev/null
+++ b/invoice/src/main/resources/com/ning/billing/invoice/dao/CreditInvoiceItemSqlDao.sql.stg
@@ -0,0 +1,69 @@
+group CreditInvoiceItemSqlDao;
+
+fields(prefix) ::= <<
+  <prefix>id,
+  <prefix>invoice_id,
+  <prefix>account_id,
+  <prefix>credit_date,
+  <prefix>amount,
+  <prefix>currency,
+  <prefix>created_by,
+  <prefix>created_date
+>>
+
+getById() ::= <<
+  SELECT <fields()>
+  FROM credit_invoice_items
+  WHERE id = :id;
+>>
+
+getInvoiceItemsByInvoice() ::= <<
+  SELECT <fields()>
+  FROM credit_invoice_items
+  WHERE invoice_id = :invoiceId;
+>>
+
+getInvoiceItemsByAccount() ::= <<
+  SELECT <fields("cii.")>
+  FROM credit_invoice_items cii
+  INNER JOIN invoices i ON i.id = cii.invoice_id
+  WHERE i.account_id = :accountId;
+>>
+
+create() ::= <<
+  INSERT INTO credit_invoice_items(<fields()>)
+  VALUES(:id, :invoiceId, :accountId, :creditDate, :amount, :currency, :userName, :createdDate);
+>>
+
+batchCreateFromTransaction() ::= <<
+  INSERT INTO credit_invoice_items(<fields()>)
+  VALUES(:id, :invoiceId, :accountId, :creditDate, :amount, :currency, :userName, :createdDate);
+>>
+
+getRecordIds() ::= <<
+    SELECT record_id, id
+    FROM credit_invoice_items
+    WHERE invoice_id = :invoiceId;
+>>
+
+auditFields(prefix) ::= <<
+    <prefix>table_name,
+    <prefix>record_id,
+    <prefix>change_type,
+    <prefix>change_date,
+    <prefix>changed_by,
+    <prefix>reason_code,
+    <prefix>comments,
+    <prefix>user_token
+>>
+
+insertAuditFromTransaction() ::= <<
+    INSERT INTO audit_log(<auditFields()>)
+    VALUES(:tableName, :recordId, :changeType, :createdDate, :userName, :reasonCode, :comment, :userToken);
+>>
+
+test() ::= <<
+  SELECT 1
+  FROM credit_invoice_items;
+>>
+;
\ No newline at end of file
diff --git a/invoice/src/main/resources/com/ning/billing/invoice/dao/InvoicePaymentSqlDao.sql.stg b/invoice/src/main/resources/com/ning/billing/invoice/dao/InvoicePaymentSqlDao.sql.stg
index 5ec38d3..3773a1c 100644
--- a/invoice/src/main/resources/com/ning/billing/invoice/dao/InvoicePaymentSqlDao.sql.stg
+++ b/invoice/src/main/resources/com/ning/billing/invoice/dao/InvoicePaymentSqlDao.sql.stg
@@ -7,18 +7,21 @@ invoicePaymentFields(prefix) ::= <<
   <prefix>payment_attempt_date,
   <prefix>amount,
   <prefix>currency,
+  <prefix>reversed_invoice_payment_id,
   <prefix>created_by,
   <prefix>created_date
 >>
 
 create() ::= <<
   INSERT INTO invoice_payments(<invoicePaymentFields()>)
-  VALUES(:id, :invoiceId, :paymentAttemptId, :paymentAttemptDate, :amount, :currency, :userName, :createdDate);
+  VALUES(:id, :invoiceId, :paymentAttemptId, :paymentAttemptDate, :amount, :currency,
+         :reversedInvoicePaymentId, :userName, :createdDate);
 >>
 
 batchCreateFromTransaction() ::= <<
   INSERT INTO invoice_payments(<invoicePaymentFields()>)
-  VALUES(:id, :invoiceId, :paymentAttemptId, :paymentAttemptDate, :amount, :currency, :userName, :createdDate);
+  VALUES(:id, :invoiceId, :paymentAttemptId, :paymentAttemptDate, :amount, :currency,
+        :reversedInvoicePaymentId, :userName, :createdDate);
 >>
 
 getByPaymentAttemptId() ::= <<
@@ -32,6 +35,12 @@ get() ::= <<
   FROM invoice_payments;
 >>
 
+getById() ::= <<
+  SELECT <invoicePaymentFields()>
+  FROM invoice_payments
+  WHERE id = :id;
+>>
+
 getPaymentsForInvoice() ::= <<
   SELECT <invoicePaymentFields()>
   FROM invoice_payments
@@ -40,7 +49,8 @@ getPaymentsForInvoice() ::= <<
 
 notifyOfPaymentAttempt() ::= <<
   INSERT INTO invoice_payments(<invoicePaymentFields()>)
-  VALUES(:id, :invoiceId, :paymentAttemptId, :paymentAttemptDate, :amount, :currency, :userName, :createdDate);
+  VALUES(:id, :invoiceId, :paymentAttemptId, :paymentAttemptDate, :amount, :currency,
+        :reversedInvoicePaymentId, :userName, :createdDate);
 >>
 
 getInvoicePayment() ::= <<
@@ -78,6 +88,35 @@ insertAuditFromTransaction() ::= <<
 >>
 
 test() ::= <<
-  SELECT 1 FROM invoice_payments;
+    SELECT 1 FROM invoice_payments;
+>>
+
+getRemainingAmountPaid() ::= <<
+    SELECT SUM(amount)
+    FROM invoice_payments
+    WHERE id = :invoicePaymentId
+    OR reversed_invoice_payment_id = :invoicePaymentId;
+>>
+
+getAccountIdFromInvoicePaymentId() ::= <<
+    SELECT account_id
+    FROM invoice_payments ip
+    INNER JOIN invoices i ON i.id = ip.invoice_id
+    WHERE ip.id = :invoicePaymentId;
+>>
+
+getChargeBacksByAccountId() ::= <<
+    SELECT <invoicePaymentFields("ip.")>
+    FROM invoice_payments ip
+    INNER JOIN invoices i ON i.id = ip.invoice_id
+    WHERE i.account_id = :accountId
+    AND reversed_invoice_payment_id IS NOT NULL;
+>>
+
+getChargebacksByAttemptPaymentId() ::= <<
+    SELECT <invoicePaymentFields()>
+    FROM invoice_payments
+    WHERE reversed_invoice_payment_id IN
+        (SELECT id FROM invoice_payments WHERE payment_attempt_id = :paymentAttemptId);
 >>
 ;
\ No newline at end of file
diff --git a/invoice/src/main/resources/com/ning/billing/invoice/dao/InvoiceSqlDao.sql.stg b/invoice/src/main/resources/com/ning/billing/invoice/dao/InvoiceSqlDao.sql.stg
index 6db5db5..fe942a8 100644
--- a/invoice/src/main/resources/com/ning/billing/invoice/dao/InvoiceSqlDao.sql.stg
+++ b/invoice/src/main/resources/com/ning/billing/invoice/dao/InvoiceSqlDao.sql.stg
@@ -39,11 +39,11 @@ getInvoicesByAccountAfterDate() ::= <<
 >>
 
 getInvoicesBySubscription() ::= <<
-  SELECT record_id as invoice_number, <invoiceFields("i.")>
+  SELECT i.record_id as invoice_number, <invoiceFields("i.")>
   FROM invoices i
   LEFT JOIN recurring_invoice_items rii ON i.id = rii.invoice_id
-  WHERE rii.subscription_id = :subscriptionId  AND migrated = 'FALSE'
-  GROUP BY record_id as invoice_number, <invoiceFields("i.")>;
+  WHERE rii.subscription_id = :subscriptionId AND migrated = 'FALSE'
+  GROUP BY i.record_id, <invoiceFields("i.")>;
 >>
 
 getById() ::= <<
diff --git a/invoice/src/main/resources/com/ning/billing/invoice/ddl.sql b/invoice/src/main/resources/com/ning/billing/invoice/ddl.sql
index 8bda35c..ee67f37 100644
--- a/invoice/src/main/resources/com/ning/billing/invoice/ddl.sql
+++ b/invoice/src/main/resources/com/ning/billing/invoice/ddl.sql
@@ -45,6 +45,22 @@ CREATE UNIQUE INDEX fixed_invoice_items_id ON fixed_invoice_items(id);
 CREATE INDEX fixed_invoice_items_subscription_id ON fixed_invoice_items(subscription_id ASC);
 CREATE INDEX fixed_invoice_items_invoice_id ON fixed_invoice_items(invoice_id ASC);
 
+DROP TABLE IF EXISTS credit_invoice_items;
+CREATE TABLE credit_invoice_items (
+    record_id int(11) unsigned NOT NULL AUTO_INCREMENT,
+    id char(36) NOT NULL,
+    invoice_id char(36) NOT NULL,
+    account_id char(36) NOT NULL,
+    credit_date datetime NOT NULL,
+    amount numeric(10,4) NULL,
+    currency char(3) NOT NULL,
+    created_by varchar(50) NOT NULL,
+    created_date datetime NOT NULL,
+    PRIMARY KEY(record_id)
+) ENGINE=innodb;
+CREATE UNIQUE INDEX credit_invoice_items_id ON credit_invoice_items(id);
+CREATE INDEX credit_invoice_items_invoice_id ON credit_invoice_items(invoice_id ASC);
+
 DROP TABLE IF EXISTS invoice_locking;
 
 DROP TABLE IF EXISTS invoices;
@@ -68,16 +84,17 @@ CREATE TABLE invoice_payments (
     record_id int(11) unsigned NOT NULL AUTO_INCREMENT,
     id char(36) NOT NULL,
     invoice_id char(36) NOT NULL,
-    payment_attempt_id char(36) COLLATE utf8_bin NOT NULL,
-    payment_attempt_date datetime,
-    amount numeric(10,4),
-    currency char(3),
+    payment_attempt_id char(36) COLLATE utf8_bin,
+    payment_attempt_date datetime NOT NULL,
+    amount numeric(10,4) NOT NULL,
+    currency char(3) NOT NULL,
+    reversed_invoice_payment_id char(36) DEFAULT NULL,
     created_by varchar(50) NOT NULL,
     created_date datetime NOT NULL,
     PRIMARY KEY(record_id)
 ) ENGINE=innodb;
 CREATE UNIQUE INDEX invoice_payments_id ON invoice_payments(id);
-CREATE UNIQUE INDEX invoice_payments_unique ON invoice_payments(invoice_id, payment_attempt_id);
+CREATE INDEX invoice_payments_reversals ON invoice_payments(reversed_invoice_payment_id);
 
 DROP VIEW IF EXISTS invoice_payment_summary;
 CREATE VIEW invoice_payment_summary AS
diff --git a/invoice/src/test/java/com/ning/billing/invoice/api/migration/TestDefaultInvoiceMigrationApi.java b/invoice/src/test/java/com/ning/billing/invoice/api/migration/TestDefaultInvoiceMigrationApi.java
index efedbaf..05f7179 100644
--- a/invoice/src/test/java/com/ning/billing/invoice/api/migration/TestDefaultInvoiceMigrationApi.java
+++ b/invoice/src/test/java/com/ning/billing/invoice/api/migration/TestDefaultInvoiceMigrationApi.java
@@ -19,11 +19,8 @@ package com.ning.billing.invoice.api.migration;
 import java.math.BigDecimal;
 import java.util.Collection;
 import java.util.List;
-import java.util.SortedSet;
-import java.util.TreeSet;
 import java.util.UUID;
 
-import com.ning.billing.invoice.tests.InvoicingTestBase;
 import org.apache.commons.io.IOUtils;
 import org.joda.time.DateTime;
 import org.slf4j.Logger;
@@ -45,10 +42,10 @@ import com.ning.billing.catalog.api.Plan;
 import com.ning.billing.catalog.api.PlanPhase;
 import com.ning.billing.dbi.MysqlTestingHelper;
 import com.ning.billing.entitlement.api.SubscriptionTransitionType;
-import com.ning.billing.entitlement.api.billing.BillingEvent;
 import com.ning.billing.entitlement.api.billing.BillingModeType;
 import com.ning.billing.entitlement.api.user.Subscription;
 import com.ning.billing.invoice.InvoiceDispatcher;
+import com.ning.billing.invoice.MockBillingEventSet;
 import com.ning.billing.invoice.TestInvoiceDispatcher;
 import com.ning.billing.invoice.api.Invoice;
 import com.ning.billing.invoice.api.InvoiceMigrationApi;
@@ -58,7 +55,9 @@ import com.ning.billing.invoice.api.InvoiceUserApi;
 import com.ning.billing.invoice.dao.InvoiceDao;
 import com.ning.billing.invoice.model.InvoiceGenerator;
 import com.ning.billing.invoice.notification.NullInvoiceNotifier;
+import com.ning.billing.invoice.tests.InvoicingTestBase;
 import com.ning.billing.junction.api.BillingApi;
+import com.ning.billing.junction.api.BillingEventSet;
 import com.ning.billing.mock.BrainDeadProxyFactory;
 import com.ning.billing.mock.BrainDeadProxyFactory.ZombieControl;
 import com.ning.billing.util.bus.BusService;
@@ -179,7 +178,7 @@ public class TestDefaultInvoiceMigrationApi extends InvoicingTestBase {
 		Subscription subscription =  BrainDeadProxyFactory.createBrainDeadProxyFor(Subscription.class);
         ((ZombieControl)subscription).addResult("getId", subscriptionId);
         ((ZombieControl)subscription).addResult("getBundleId", new UUID(0L,0L));
-		SortedSet<BillingEvent> events = new TreeSet<BillingEvent>();
+		BillingEventSet events = new MockBillingEventSet();
 		Plan plan = MockPlan.createBicycleNoTrialEvergreen1USD();
 		PlanPhase planPhase = MockPlanPhase.create1USDMonthlyEvergreen();
 		DateTime effectiveDate = new DateTime().minusDays(1);
diff --git a/invoice/src/test/java/com/ning/billing/invoice/api/MockInvoicePaymentApi.java b/invoice/src/test/java/com/ning/billing/invoice/api/MockInvoicePaymentApi.java
index 09b809c..1bde0c6 100644
--- a/invoice/src/test/java/com/ning/billing/invoice/api/MockInvoicePaymentApi.java
+++ b/invoice/src/test/java/com/ning/billing/invoice/api/MockInvoicePaymentApi.java
@@ -27,6 +27,7 @@ import org.joda.time.DateTime;
 import com.ning.billing.catalog.api.Currency;
 import com.ning.billing.invoice.model.DefaultInvoicePayment;
 import com.ning.billing.util.callcontext.CallContext;
+import org.joda.time.DateTimeZone;
 
 public class MockInvoicePaymentApi implements InvoicePaymentApi
 {
@@ -101,4 +102,67 @@ public class MockInvoicePaymentApi implements InvoicePaymentApi
         notifyOfPaymentAttempt(invoicePayment, context);
     }
 
+    @Override
+    public void processChargeback(UUID invoicePaymentId, BigDecimal amount, CallContext context) throws InvoiceApiException {
+        InvoicePayment existingPayment = null;
+        for (InvoicePayment payment : invoicePayments) {
+            if (payment.getId()  == invoicePaymentId) {
+                existingPayment = payment;
+            }
+        }
+
+        if (existingPayment != null) {
+            invoicePayments.add(existingPayment.asChargeBack(amount, DateTime.now(DateTimeZone.UTC)));
+        }
+    }
+
+    @Override
+    public void processChargeback(UUID invoicePaymentId, CallContext context) throws InvoiceApiException {
+        InvoicePayment existingPayment = null;
+        for (InvoicePayment payment : invoicePayments) {
+            if (payment.getId()  == invoicePaymentId) {
+                existingPayment = payment;
+            }
+        }
+
+        if (existingPayment != null) {
+            this.processChargeback(invoicePaymentId, existingPayment.getAmount(), context);
+        }
+    }
+
+    @Override
+    public BigDecimal getRemainingAmountPaid(UUID invoicePaymentId) {
+        BigDecimal amount = BigDecimal.ZERO;
+        for (InvoicePayment payment : invoicePayments) {
+            if (payment.getId().equals(invoicePaymentId)) {
+                amount = amount.add(payment.getAmount());
+            }
+
+            if (payment.getReversedInvoicePaymentId().equals(invoicePaymentId)) {
+                amount = amount.add(payment.getAmount());
+            }
+        }
+
+        return amount;
+    }
+
+    @Override
+    public List<InvoicePayment> getChargebacksByAccountId(UUID accountId) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public UUID getAccountIdFromInvoicePaymentId(UUID uuid) throws InvoiceApiException {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public List<InvoicePayment> getChargebacksByPaymentAttemptId(UUID paymentAttemptId) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public InvoicePayment getChargebackById(UUID chargebackId) {
+        throw new UnsupportedOperationException();
+    }
 }
diff --git a/invoice/src/test/java/com/ning/billing/invoice/dao/InvoiceDaoTestBase.java b/invoice/src/test/java/com/ning/billing/invoice/dao/InvoiceDaoTestBase.java
index 0e99dab..afba58b 100644
--- a/invoice/src/test/java/com/ning/billing/invoice/dao/InvoiceDaoTestBase.java
+++ b/invoice/src/test/java/com/ning/billing/invoice/dao/InvoiceDaoTestBase.java
@@ -20,6 +20,14 @@ import static org.testng.Assert.assertTrue;
 
 import java.io.IOException;
 
+import com.ning.billing.dbi.MysqlTestingHelper;
+import com.ning.billing.invoice.notification.MockNextBillingDatePoster;
+import com.ning.billing.invoice.notification.NextBillingDatePoster;
+import com.ning.billing.util.callcontext.TestCallContext;
+import com.ning.billing.util.clock.ClockMock;
+import com.ning.billing.util.tag.dao.AuditedTagDao;
+import com.ning.billing.util.tag.dao.MockTagDao;
+import com.ning.billing.util.tag.dao.TagDao;
 import org.apache.commons.io.IOUtils;
 import org.skife.jdbi.v2.Handle;
 import org.skife.jdbi.v2.IDBI;
@@ -29,28 +37,21 @@ import org.testng.annotations.AfterClass;
 import org.testng.annotations.BeforeClass;
 import org.testng.annotations.BeforeMethod;
 
-import com.google.inject.Guice;
-import com.google.inject.Injector;
-import com.google.inject.Stage;
 import com.ning.billing.config.InvoiceConfig;
-import com.ning.billing.invoice.glue.InvoiceModuleWithEmbeddedDb;
 import com.ning.billing.invoice.model.DefaultInvoiceGenerator;
 import com.ning.billing.invoice.model.InvoiceGenerator;
 import com.ning.billing.invoice.tests.InvoicingTestBase;
-import com.ning.billing.util.bus.BusService;
-import com.ning.billing.util.bus.DefaultBusService;
 import com.ning.billing.util.callcontext.CallContext;
-import com.ning.billing.util.callcontext.CallOrigin;
-import com.ning.billing.util.callcontext.DefaultCallContextFactory;
-import com.ning.billing.util.callcontext.UserType;
 import com.ning.billing.util.clock.Clock;
 
 public abstract class InvoiceDaoTestBase extends InvoicingTestBase {
     protected IDBI dbi;
+    private MysqlTestingHelper mysqlTestingHelper;
     protected InvoiceDao invoiceDao;
     protected RecurringInvoiceItemSqlDao recurringInvoiceItemDao;
+    protected FixedPriceInvoiceItemSqlDao fixedPriceInvoiceItemSqlDao;
+    protected CreditInvoiceItemSqlDao creditInvoiceItemSqlDao;
     protected InvoicePaymentSqlDao invoicePaymentDao;
-    protected InvoiceModuleWithEmbeddedDb module;
     protected Clock clock;
     protected CallContext context;
     protected InvoiceGenerator generator;
@@ -66,44 +67,44 @@ public abstract class InvoiceDaoTestBase extends InvoicingTestBase {
 
     @BeforeClass(alwaysRun = true)
     protected void setup() throws IOException {
-        module = new InvoiceModuleWithEmbeddedDb();
-        dbi = module.getDbi();
+        mysqlTestingHelper = new MysqlTestingHelper();
+        dbi = mysqlTestingHelper.getDBI();
 
         final String invoiceDdl = IOUtils.toString(DefaultInvoiceDao.class.getResourceAsStream("/com/ning/billing/invoice/ddl.sql"));
         final String utilDdl = IOUtils.toString(DefaultInvoiceDao.class.getResourceAsStream("/com/ning/billing/util/ddl.sql"));
 
-        module.startDb();
-        module.initDb(invoiceDdl);
-        module.initDb(utilDdl);
+        mysqlTestingHelper.startMysql();
+        mysqlTestingHelper.initDb(invoiceDdl);
+        mysqlTestingHelper.initDb(utilDdl);
 
-        final Injector injector = Guice.createInjector(Stage.DEVELOPMENT, module);
-
-        invoiceDao = injector.getInstance(InvoiceDao.class);
+        NextBillingDatePoster nextBillingDatePoster = new MockNextBillingDatePoster();
+        TagDao tagDao = new AuditedTagDao(dbi);
+        invoiceDao = new DefaultInvoiceDao(dbi, nextBillingDatePoster, tagDao);
         invoiceDao.test();
 
-        recurringInvoiceItemDao = module.getInvoiceItemSqlDao();
+        recurringInvoiceItemDao = dbi.onDemand(RecurringInvoiceItemSqlDao.class);
+        fixedPriceInvoiceItemSqlDao = dbi.onDemand(FixedPriceInvoiceItemSqlDao.class);
+        creditInvoiceItemSqlDao = dbi.onDemand(CreditInvoiceItemSqlDao.class);
+        invoicePaymentDao = dbi.onDemand(InvoicePaymentSqlDao.class);
 
-        invoicePaymentDao = module.getInvoicePaymentSqlDao();
-        clock = injector.getInstance(Clock.class);
-        context = new DefaultCallContextFactory(clock).createCallContext("Count Rogan", CallOrigin.TEST, UserType.TEST);
+        clock = new ClockMock();
+        context = new TestCallContext("Invoice Dao Tests");
         generator = new DefaultInvoiceGenerator(clock, invoiceConfig);
 
-        BusService busService = injector.getInstance(BusService.class);
-        ((DefaultBusService) busService).startBus();
-
         assertTrue(true);
-       
     }
 
     @BeforeMethod(alwaysRun = true)
     public void cleanupData() {
-        module.getDbi().inTransaction(new TransactionCallback<Void>() {
+        dbi.inTransaction(new TransactionCallback<Void>() {
             @Override
             public Void inTransaction(Handle h, TransactionStatus status)
                     throws Exception {
                 h.execute("truncate table invoices");
                 h.execute("truncate table fixed_invoice_items");
                 h.execute("truncate table recurring_invoice_items");
+                h.execute("truncate table credit_invoice_items");
+                h.execute("truncate table invoice_payments");
 
                 return null;
             }
@@ -112,7 +113,7 @@ public abstract class InvoiceDaoTestBase extends InvoicingTestBase {
 
     @AfterClass(alwaysRun = true)
     protected void tearDown() {
-        module.stopDb();
+        mysqlTestingHelper.stopMysql();
         assertTrue(true);
     }
 }
diff --git a/invoice/src/test/java/com/ning/billing/invoice/dao/InvoiceDaoTests.java b/invoice/src/test/java/com/ning/billing/invoice/dao/InvoiceDaoTests.java
index 809c141..97d7532 100644
--- a/invoice/src/test/java/com/ning/billing/invoice/dao/InvoiceDaoTests.java
+++ b/invoice/src/test/java/com/ning/billing/invoice/dao/InvoiceDaoTests.java
@@ -28,10 +28,6 @@ import java.util.List;
 import java.util.Map;
 import java.util.UUID;
 
-import com.ning.billing.util.dao.ObjectType;
-import com.ning.billing.util.tag.Tag;
-import com.ning.billing.util.tag.dao.AuditedTagDao;
-import com.ning.billing.util.tag.dao.TagDao;
 import org.joda.time.DateTime;
 import org.testng.annotations.Test;
 
@@ -49,17 +45,22 @@ import com.ning.billing.entitlement.api.SubscriptionTransitionType;
 import com.ning.billing.entitlement.api.billing.BillingEvent;
 import com.ning.billing.entitlement.api.billing.BillingModeType;
 import com.ning.billing.entitlement.api.user.Subscription;
+import com.ning.billing.invoice.MockBillingEventSet;
 import com.ning.billing.invoice.api.Invoice;
 import com.ning.billing.invoice.api.InvoiceApiException;
 import com.ning.billing.invoice.api.InvoiceItem;
 import com.ning.billing.invoice.api.InvoicePayment;
-import com.ning.billing.invoice.model.BillingEventSet;
 import com.ning.billing.invoice.model.DefaultInvoice;
 import com.ning.billing.invoice.model.DefaultInvoicePayment;
 import com.ning.billing.invoice.model.RecurringInvoiceItem;
+import com.ning.billing.junction.api.BillingEventSet;
 import com.ning.billing.mock.BrainDeadProxyFactory;
 import com.ning.billing.mock.BrainDeadProxyFactory.ZombieControl;
+import com.ning.billing.util.dao.ObjectType;
 import com.ning.billing.util.tag.ControlTagType;
+import com.ning.billing.util.tag.Tag;
+import com.ning.billing.util.tag.dao.AuditedTagDao;
+import com.ning.billing.util.tag.dao.TagDao;
 
 @Test(groups = {"slow", "invoicing", "invoicing-invoiceDao"})
 public class InvoiceDaoTests extends InvoiceDaoTestBase {
@@ -79,7 +80,7 @@ public class InvoiceDaoTests extends InvoiceDaoTestBase {
         assertTrue(thisInvoice.getInvoiceDate().compareTo(invoiceDate) == 0);
         assertEquals(thisInvoice.getCurrency(), Currency.USD);
         assertEquals(thisInvoice.getNumberOfItems(), 0);
-        assertTrue(thisInvoice.getTotalAmount().compareTo(BigDecimal.ZERO) == 0);
+        assertTrue(thisInvoice.getBalance().compareTo(BigDecimal.ZERO) == 0);
     }
 
     @Test
@@ -99,7 +100,7 @@ public class InvoiceDaoTests extends InvoiceDaoTestBase {
 
         Invoice savedInvoice = invoiceDao.getById(invoiceId);
         assertNotNull(savedInvoice);
-        assertEquals(savedInvoice.getTotalAmount().compareTo(new BigDecimal("21.00")), 0);
+        assertEquals(savedInvoice.getBalance().compareTo(new BigDecimal("21.00")), 0);
         assertEquals(savedInvoice.getBalance().compareTo(new BigDecimal("21.00")), 0);
         assertEquals(savedInvoice.getAmountPaid(), BigDecimal.ZERO);
         assertEquals(savedInvoice.getInvoiceItems().size(), 1);
@@ -112,7 +113,7 @@ public class InvoiceDaoTests extends InvoiceDaoTestBase {
         Invoice retrievedInvoice = invoiceDao.getById(invoiceId);
         assertNotNull(retrievedInvoice);
         assertEquals(retrievedInvoice.getInvoiceItems().size(), 1);
-        assertEquals(retrievedInvoice.getTotalAmount().compareTo(new BigDecimal("21.00")), 0);
+        assertEquals(retrievedInvoice.getAmountCharged().compareTo(new BigDecimal("21.00")), 0);
         assertEquals(retrievedInvoice.getBalance().compareTo(new BigDecimal("10.00")), 0);
         assertEquals(retrievedInvoice.getAmountPaid().compareTo(new BigDecimal("11.00")), 0);
     }
@@ -417,7 +418,7 @@ public class InvoiceDaoTests extends InvoiceDaoTestBase {
                 recurringPrice.getPrice(currency), currency, BillingPeriod.MONTHLY, 1, BillingModeType.IN_ADVANCE,
                 "testEvent1", 1L, SubscriptionTransitionType.CREATE);
 
-        BillingEventSet events = new BillingEventSet();
+        BillingEventSet events = new MockBillingEventSet();
         events.add(event1);
 
         Invoice invoice1 = generator.generateInvoice(accountId, events, invoiceList, targetDate, Currency.USD);
@@ -446,10 +447,10 @@ public class InvoiceDaoTests extends InvoiceDaoTestBase {
         invoiceDao.create(invoice2, context);
 
         Invoice savedInvoice1 = invoiceDao.getById(invoice1.getId());
-        assertEquals(savedInvoice1.getTotalAmount(), ZERO);
+        assertEquals(savedInvoice1.getBalance(), ZERO);
 
         Invoice savedInvoice2 = invoiceDao.getById(invoice2.getId());
-        assertEquals(savedInvoice2.getTotalAmount(), FIFTEEN);
+        assertEquals(savedInvoice2.getBalance(), FIFTEEN);
     }
 
     @Test
@@ -466,7 +467,7 @@ public class InvoiceDaoTests extends InvoiceDaoTestBase {
         BillingEvent event = createMockBillingEvent(null, subscription, effectiveDate, plan, phase, null,
                 recurringPrice.getPrice(currency), currency, BillingPeriod.MONTHLY, 15, BillingModeType.IN_ADVANCE,
                 "testEvent", 1L, SubscriptionTransitionType.CREATE);
-        BillingEventSet events = new BillingEventSet();
+        BillingEventSet events = new MockBillingEventSet();
         events.add(event);
 
         DateTime targetDate = buildDateTime(2011, 1, 15);
@@ -474,7 +475,7 @@ public class InvoiceDaoTests extends InvoiceDaoTestBase {
 
         // expect one pro-ration item and one full-period item
         assertEquals(invoice.getNumberOfItems(), 2);
-        assertEquals(invoice.getTotalAmount().compareTo(ZERO), 0);
+        assertEquals(invoice.getBalance().compareTo(ZERO), 0);
     }
 
     private Subscription getZombieSubscription() {
@@ -505,14 +506,14 @@ public class InvoiceDaoTests extends InvoiceDaoTestBase {
         BillingEvent event1 = createMockBillingEvent(null, subscription, effectiveDate1, plan, phase1, fixedPrice.getPrice(currency),
                 null, currency, BillingPeriod.MONTHLY, 1, BillingModeType.IN_ADVANCE,
                 "testEvent1", 1L, SubscriptionTransitionType.CREATE);
-        BillingEventSet events = new BillingEventSet();
+        BillingEventSet events = new MockBillingEventSet();
         events.add(event1);
 
         UUID accountId = UUID.randomUUID();
         Invoice invoice1 = generator.generateInvoice(accountId, events, null, effectiveDate1, Currency.USD);
         assertNotNull(invoice1);
         assertEquals(invoice1.getNumberOfItems(), 1);
-        assertEquals(invoice1.getTotalAmount().compareTo(ZERO), 0);
+        assertEquals(invoice1.getBalance().compareTo(ZERO), 0);
 
         List<Invoice> invoiceList = new ArrayList<Invoice>();
         invoiceList.add(invoice1);
@@ -526,7 +527,7 @@ public class InvoiceDaoTests extends InvoiceDaoTestBase {
         Invoice invoice2 = generator.generateInvoice(accountId, events, invoiceList, effectiveDate2, Currency.USD);
         assertNotNull(invoice2);
         assertEquals(invoice2.getNumberOfItems(), 1);
-        assertEquals(invoice2.getTotalAmount().compareTo(cheapAmount), 0);
+        assertEquals(invoice2.getBalance().compareTo(cheapAmount), 0);
 
         invoiceList.add(invoice2);
 
@@ -534,12 +535,12 @@ public class InvoiceDaoTests extends InvoiceDaoTestBase {
         Invoice invoice3 = generator.generateInvoice(accountId, events, invoiceList, effectiveDate3, Currency.USD);
         assertNotNull(invoice3);
         assertEquals(invoice3.getNumberOfItems(), 1);
-        assertEquals(invoice3.getTotalAmount().compareTo(cheapAmount), 0);
+        assertEquals(invoice3.getBalance().compareTo(cheapAmount), 0);
     }
 
     @Test
     public void testInvoiceForEmptyEventSet() throws InvoiceApiException {
-        BillingEventSet events = new BillingEventSet();
+        BillingEventSet events = new MockBillingEventSet();
         Invoice invoice = generator.generateInvoice(UUID.randomUUID(), events, null, new DateTime(), Currency.USD);
         assertNull(invoice);
     }
@@ -565,7 +566,7 @@ public class InvoiceDaoTests extends InvoiceDaoTestBase {
                 fixedPrice.getPrice(currency), null, currency,
                 BillingPeriod.MONTHLY, 1, BillingModeType.IN_ADVANCE,
                 "testEvent1", 1L, SubscriptionTransitionType.CREATE);
-        BillingEventSet events = new BillingEventSet();
+        BillingEventSet events = new MockBillingEventSet();
         events.add(event1);
 
         DateTime effectiveDate2 = effectiveDate1.plusDays(30);
@@ -577,14 +578,14 @@ public class InvoiceDaoTests extends InvoiceDaoTestBase {
         Invoice invoice = generator.generateInvoice(UUID.randomUUID(), events, null, effectiveDate2, Currency.USD);
         assertNotNull(invoice);
         assertEquals(invoice.getNumberOfItems(), 2);
-        assertEquals(invoice.getTotalAmount().compareTo(cheapAmount), 0);
+        assertEquals(invoice.getBalance().compareTo(cheapAmount), 0);
 
         invoiceDao.create(invoice, context);
         Invoice savedInvoice = invoiceDao.getById(invoice.getId());
 
         assertNotNull(savedInvoice);
         assertEquals(savedInvoice.getNumberOfItems(), 2);
-        assertEquals(savedInvoice.getTotalAmount().compareTo(cheapAmount), 0);
+        assertEquals(savedInvoice.getBalance().compareTo(cheapAmount), 0);
     }
 
     @Test
@@ -604,7 +605,7 @@ public class InvoiceDaoTests extends InvoiceDaoTestBase {
         PlanPhase phase2 = BrainDeadProxyFactory.createBrainDeadProxyFor(PlanPhase.class);
         ((ZombieControl) phase2).addResult("getName", "plan-phase2");
 
-        BillingEventSet events = new BillingEventSet();
+        BillingEventSet events = new MockBillingEventSet();
         List<Invoice> invoices = new ArrayList<Invoice>();
 
         BillingEvent event1 = createMockBillingEvent(null, subscription, targetDate1, plan, phase1, null,
@@ -648,7 +649,7 @@ public class InvoiceDaoTests extends InvoiceDaoTestBase {
                 TEN, currency,
                 BillingPeriod.MONTHLY, 31, BillingModeType.IN_ADVANCE,
                 "testEvent1", 1L, SubscriptionTransitionType.CHANGE);
-        BillingEventSet events = new BillingEventSet();
+        BillingEventSet events = new MockBillingEventSet();
         events.add(event1);
 
         Invoice invoice = generator.generateInvoice(UUID.randomUUID(), events, null, targetDate1, Currency.USD);
@@ -679,7 +680,7 @@ public class InvoiceDaoTests extends InvoiceDaoTestBase {
                 TEN, currency,
                 BillingPeriod.MONTHLY, 31, BillingModeType.IN_ADVANCE,
                 "testEvent1", 1L, SubscriptionTransitionType.CHANGE);
-        BillingEventSet events = new BillingEventSet();
+        BillingEventSet events = new MockBillingEventSet();
         events.add(event1);
 
         Invoice invoice = generator.generateInvoice(UUID.randomUUID(), events, null, targetDate1, Currency.USD);
diff --git a/invoice/src/test/java/com/ning/billing/invoice/dao/InvoiceItemDaoTests.java b/invoice/src/test/java/com/ning/billing/invoice/dao/InvoiceItemDaoTests.java
index 9acde8e..c97e775 100644
--- a/invoice/src/test/java/com/ning/billing/invoice/dao/InvoiceItemDaoTests.java
+++ b/invoice/src/test/java/com/ning/billing/invoice/dao/InvoiceItemDaoTests.java
@@ -18,11 +18,15 @@ package com.ning.billing.invoice.dao;
 
 import static org.testng.Assert.assertEquals;
 import static org.testng.Assert.assertNotNull;
+import static org.testng.Assert.assertTrue;
+import static org.testng.Assert.fail;
 
 import java.math.BigDecimal;
 import java.util.List;
 import java.util.UUID;
 
+import com.ning.billing.invoice.model.CreditInvoiceItem;
+import com.ning.billing.invoice.model.FixedPriceInvoiceItem;
 import org.joda.time.DateTime;
 import org.testng.annotations.Test;
 
@@ -31,9 +35,9 @@ import com.ning.billing.invoice.api.InvoiceItem;
 import com.ning.billing.invoice.model.DefaultInvoice;
 import com.ning.billing.invoice.model.RecurringInvoiceItem;
 
-@Test(groups = {"invoicing", "invoicing-invoiceDao"})
+@Test(groups = {"slow", "invoicing", "invoicing-invoiceDao"})
 public class InvoiceItemDaoTests extends InvoiceDaoTestBase {
-    @Test(groups = "slow")
+    @Test
     public void testInvoiceItemCreation() {
         UUID accountId = UUID.randomUUID();
         UUID invoiceId = UUID.randomUUID();
@@ -52,8 +56,8 @@ public class InvoiceItemDaoTests extends InvoiceDaoTestBase {
         assertEquals(thisItem.getId(), item.getId());
         assertEquals(thisItem.getInvoiceId(), item.getInvoiceId());
         assertEquals(thisItem.getSubscriptionId(), item.getSubscriptionId());
-        assertEquals(thisItem.getStartDate(), item.getStartDate());
-        assertEquals(thisItem.getEndDate(), item.getEndDate());
+        assertTrue(thisItem.getStartDate().compareTo(item.getStartDate()) == 0);
+        assertTrue(thisItem.getEndDate().compareTo(item.getEndDate()) == 0);
         assertEquals(thisItem.getAmount().compareTo(item.getRate()), 0);
         assertEquals(thisItem.getRate().compareTo(item.getRate()), 0);
         assertEquals(thisItem.getCurrency(), item.getCurrency());
@@ -61,7 +65,7 @@ public class InvoiceItemDaoTests extends InvoiceDaoTestBase {
         // assertEquals(thisItem.getCreatedDate().compareTo(item.getCreatedDate()), 0);
     }
 
-    @Test(groups = "slow")
+    @Test
     public void testGetInvoiceItemsBySubscriptionId() {
         UUID accountId = UUID.randomUUID();
         UUID subscriptionId = UUID.randomUUID();
@@ -82,7 +86,7 @@ public class InvoiceItemDaoTests extends InvoiceDaoTestBase {
         assertEquals(items.size(), 3);
     }
 
-    @Test(groups = "slow")
+    @Test
     public void testGetInvoiceItemsByInvoiceId() {
         UUID accountId = UUID.randomUUID();
         UUID invoiceId = UUID.randomUUID();
@@ -104,7 +108,7 @@ public class InvoiceItemDaoTests extends InvoiceDaoTestBase {
         assertEquals(items.size(), 5);
     }
 
-    @Test(groups = "slow")
+    @Test
     public void testGetInvoiceItemsByAccountId() {
         UUID accountId = UUID.randomUUID();
         UUID bundleId = UUID.randomUUID();
@@ -127,4 +131,31 @@ public class InvoiceItemDaoTests extends InvoiceDaoTestBase {
         List<InvoiceItem> items = recurringInvoiceItemDao.getInvoiceItemsByAccount(accountId.toString());
         assertEquals(items.size(), 1);
     }
+
+    @Test
+    public void testCreditInvoiceSqlDao() {
+        UUID invoiceId = UUID.randomUUID();
+        UUID accountId = UUID.randomUUID();
+        DateTime creditDate = new DateTime(2012, 4, 1, 0, 10, 22, 0);
+
+        InvoiceItem creditInvoiceItem = new CreditInvoiceItem(invoiceId, accountId, creditDate, TEN, Currency.USD);
+        creditInvoiceItemSqlDao.create(creditInvoiceItem, context);
+
+        InvoiceItem savedItem = creditInvoiceItemSqlDao.getById(creditInvoiceItem.getId().toString());
+        assertEquals(savedItem, creditInvoiceItem);
+    }
+
+    @Test
+    public void testFixedPriceInvoiceSqlDao() {
+        UUID invoiceId = UUID.randomUUID();
+        UUID accountId = UUID.randomUUID();
+        DateTime startDate = new DateTime(2012, 4, 1, 0, 10, 22, 0);
+
+        InvoiceItem fixedPriceInvoiceItem = new FixedPriceInvoiceItem(invoiceId, accountId, UUID.randomUUID(),
+                UUID.randomUUID(), "test plan", "test phase", startDate, startDate.plusMonths(1), TEN, Currency.USD);
+        fixedPriceInvoiceItemSqlDao.create(fixedPriceInvoiceItem, context);
+
+        InvoiceItem savedItem = fixedPriceInvoiceItemSqlDao.getById(fixedPriceInvoiceItem.getId().toString());
+        assertEquals(savedItem, fixedPriceInvoiceItem);
+    }
 }
diff --git a/invoice/src/test/java/com/ning/billing/invoice/dao/MockInvoiceDao.java b/invoice/src/test/java/com/ning/billing/invoice/dao/MockInvoiceDao.java
index 2c65cd8..e24effb 100644
--- a/invoice/src/test/java/com/ning/billing/invoice/dao/MockInvoiceDao.java
+++ b/invoice/src/test/java/com/ning/billing/invoice/dao/MockInvoiceDao.java
@@ -23,6 +23,8 @@ import java.util.List;
 import java.util.Map;
 import java.util.UUID;
 
+import com.ning.billing.catalog.api.Currency;
+import com.ning.billing.invoice.api.InvoiceApiException;
 import org.joda.time.DateTime;
 
 import com.google.inject.Inject;
@@ -210,4 +212,44 @@ public class MockInvoiceDao implements InvoiceDao {
     public void removeWrittenOff(UUID objectId, CallContext context) {
         throw new UnsupportedOperationException();
     }
+
+    @Override
+    public void postChargeback(UUID invoicePaymentId, BigDecimal amount, CallContext context) throws InvoiceApiException {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public BigDecimal getRemainingAmountPaid(UUID invoicePaymentId) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public UUID getAccountIdFromInvoicePaymentId(UUID invoicePaymentId) throws InvoiceApiException {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public List<InvoicePayment> getChargebacksByAccountId(UUID accountId) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public List<InvoicePayment> getChargebacksByPaymentAttemptId(UUID paymentAttemptId) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public InvoicePayment getChargebackById(UUID chargebackId) throws InvoiceApiException {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public InvoiceItem getCreditById(UUID creditId) throws InvoiceApiException {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public InvoiceItem insertCredit(UUID accountId, BigDecimal amount, DateTime effectiveDate, Currency currency, CallContext context) {
+        throw new UnsupportedOperationException();
+    }
 }
diff --git a/invoice/src/test/java/com/ning/billing/invoice/HtmlInvoiceGeneratorTest.java b/invoice/src/test/java/com/ning/billing/invoice/HtmlInvoiceGeneratorTest.java
index 95f39c9..144fb13 100644
--- a/invoice/src/test/java/com/ning/billing/invoice/HtmlInvoiceGeneratorTest.java
+++ b/invoice/src/test/java/com/ning/billing/invoice/HtmlInvoiceGeneratorTest.java
@@ -90,7 +90,7 @@ public class HtmlInvoiceGeneratorTest {
         zombie.addResult("getInvoiceDate", startDate);
         zombie.addResult("getInvoiceNumber", 42);
         zombie.addResult("getCurrency", Currency.USD);
-        zombie.addResult("getTotalAmount", price1.add(price2));
+        zombie.addResult("getAmountCharged", price1.add(price2));
         zombie.addResult("getAmountPaid", BigDecimal.ZERO);
         zombie.addResult("getBalance", price1.add(price2));
 
diff --git a/invoice/src/test/java/com/ning/billing/invoice/MockBillingEventSet.java b/invoice/src/test/java/com/ning/billing/invoice/MockBillingEventSet.java
new file mode 100644
index 0000000..e8002f0
--- /dev/null
+++ b/invoice/src/test/java/com/ning/billing/invoice/MockBillingEventSet.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2010-2011 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.
+ */
+
+package com.ning.billing.invoice;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.TreeSet;
+import java.util.UUID;
+
+import com.ning.billing.entitlement.api.billing.BillingEvent;
+import com.ning.billing.junction.api.BillingEventSet;
+
+public class MockBillingEventSet extends TreeSet<BillingEvent> implements BillingEventSet {
+
+    private static final long serialVersionUID = 1L;
+
+    private boolean isAccountInvoiceOff;
+    private List<UUID> subscriptionIdsWithAutoInvoiceOff = new ArrayList<UUID>();
+
+    public void addSubscriptionWithAutoInvoiceOff(UUID subscriptionId) {
+        subscriptionIdsWithAutoInvoiceOff.add(subscriptionId);
+    }
+
+    @Override
+    public boolean isLast(BillingEvent event) {
+        return event == last();
+     }
+
+    @Override
+    public boolean isAccountAutoInvoiceOff() {
+        return isAccountInvoiceOff;
+    }
+
+    @Override
+    public List<UUID> getSubscriptionIdsWithAutoInvoiceOff() {
+        return subscriptionIdsWithAutoInvoiceOff;
+    }
+
+    public void setAccountInvoiceOff(boolean isAccountInvoiceOff) {
+        this.isAccountInvoiceOff = isAccountInvoiceOff;
+    }
+
+    public void setSubscriptionIdsWithAutoInvoiceOff(List<UUID> subscriptionIdsWithAutoInvoiceOff) {
+        this.subscriptionIdsWithAutoInvoiceOff = subscriptionIdsWithAutoInvoiceOff;
+    }
+
+    public void clearSubscriptionsWithAutoInvoiceOff() {
+        subscriptionIdsWithAutoInvoiceOff.clear();
+    }
+}
diff --git a/invoice/src/test/java/com/ning/billing/invoice/TestInvoiceDispatcher.java b/invoice/src/test/java/com/ning/billing/invoice/TestInvoiceDispatcher.java
index 992274d..679049a 100644
--- a/invoice/src/test/java/com/ning/billing/invoice/TestInvoiceDispatcher.java
+++ b/invoice/src/test/java/com/ning/billing/invoice/TestInvoiceDispatcher.java
@@ -16,16 +16,10 @@
 
 package com.ning.billing.invoice;
 
-import java.io.IOException;
 import java.math.BigDecimal;
 import java.util.List;
-import java.util.SortedSet;
-import java.util.TreeSet;
 import java.util.UUID;
 
-import com.ning.billing.invoice.api.InvoiceNotifier;
-import com.ning.billing.invoice.notification.NullInvoiceNotifier;
-import com.ning.billing.invoice.tests.InvoicingTestBase;
 import org.apache.commons.io.IOUtils;
 import org.joda.time.DateTime;
 import org.slf4j.Logger;
@@ -47,18 +41,20 @@ import com.ning.billing.catalog.api.Plan;
 import com.ning.billing.catalog.api.PlanPhase;
 import com.ning.billing.dbi.MysqlTestingHelper;
 import com.ning.billing.entitlement.api.SubscriptionTransitionType;
-import com.ning.billing.entitlement.api.billing.BillingEvent;
 import com.ning.billing.entitlement.api.billing.BillingModeType;
 import com.ning.billing.entitlement.api.user.Subscription;
 import com.ning.billing.invoice.api.Invoice;
 import com.ning.billing.invoice.api.InvoiceApiException;
+import com.ning.billing.invoice.api.InvoiceNotifier;
 import com.ning.billing.invoice.dao.InvoiceDao;
 import com.ning.billing.invoice.model.InvoiceGenerator;
 import com.ning.billing.invoice.notification.NextBillingDateNotifier;
+import com.ning.billing.invoice.notification.NullInvoiceNotifier;
+import com.ning.billing.invoice.tests.InvoicingTestBase;
 import com.ning.billing.junction.api.BillingApi;
+import com.ning.billing.junction.api.BillingEventSet;
 import com.ning.billing.mock.BrainDeadProxyFactory;
 import com.ning.billing.mock.BrainDeadProxyFactory.ZombieControl;
-import com.ning.billing.mock.glue.MockJunctionModule;
 import com.ning.billing.util.bus.BusService;
 import com.ning.billing.util.bus.DefaultBusService;
 import com.ning.billing.util.callcontext.CallContext;
@@ -145,7 +141,7 @@ public class TestInvoiceDispatcher extends InvoicingTestBase {
 		Subscription subscription =  BrainDeadProxyFactory.createBrainDeadProxyFor(Subscription.class);
         ((ZombieControl)subscription).addResult("getId", subscriptionId);
         ((ZombieControl)subscription).addResult("getBundleId", new UUID(0L,0L));
-		SortedSet<BillingEvent> events = new TreeSet<BillingEvent>();
+        BillingEventSet events = new MockBillingEventSet();
 		Plan plan = MockPlan.createBicycleNoTrialEvergreen1USD();
 		PlanPhase planPhase = MockPlanPhase.create1USDMonthlyEvergreen();
 		DateTime effectiveDate = new DateTime().minusDays(1);
@@ -184,5 +180,7 @@ public class TestInvoiceDispatcher extends InvoicingTestBase {
 		Assert.assertEquals(invoices.size(),1);
 
 	}
+    
+    //MDW add a test to cover when the account auto-invoice-off tag is present
 
 }
diff --git a/invoice/src/test/java/com/ning/billing/invoice/tests/ChargeBackTests.java b/invoice/src/test/java/com/ning/billing/invoice/tests/ChargeBackTests.java
new file mode 100644
index 0000000..6eac0dc
--- /dev/null
+++ b/invoice/src/test/java/com/ning/billing/invoice/tests/ChargeBackTests.java
@@ -0,0 +1,221 @@
+/*
+ * Copyright 2010-2011 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.
+ */
+
+package com.ning.billing.invoice.tests;
+
+import com.ning.billing.catalog.api.Currency;
+import com.ning.billing.dbi.MysqlTestingHelper;
+import com.ning.billing.invoice.api.Invoice;
+import com.ning.billing.invoice.api.InvoiceApiException;
+import com.ning.billing.invoice.api.InvoiceItem;
+import com.ning.billing.invoice.api.InvoicePayment;
+import com.ning.billing.invoice.api.InvoicePaymentApi;
+import com.ning.billing.invoice.api.invoice.DefaultInvoicePaymentApi;
+import com.ning.billing.invoice.dao.DefaultInvoiceDao;
+import com.ning.billing.invoice.dao.InvoiceDao;
+import com.ning.billing.invoice.dao.InvoiceSqlDao;
+import com.ning.billing.invoice.model.FixedPriceInvoiceItem;
+import com.ning.billing.invoice.notification.MockNextBillingDatePoster;
+import com.ning.billing.invoice.notification.NextBillingDatePoster;
+import com.ning.billing.mock.BrainDeadProxyFactory;
+import com.ning.billing.mock.BrainDeadProxyFactory.ZombieControl;
+import com.ning.billing.util.callcontext.CallContext;
+import com.ning.billing.util.callcontext.TestCallContext;
+import com.ning.billing.util.clock.Clock;
+import com.ning.billing.util.clock.ClockMock;
+import com.ning.billing.util.tag.dao.MockTagDao;
+import com.ning.billing.util.tag.dao.TagDao;
+import org.skife.jdbi.v2.IDBI;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
+import java.math.BigDecimal;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.UUID;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNotNull;
+import static org.testng.Assert.assertTrue;
+import static org.testng.Assert.fail;
+
+@Test(groups = {"slow", "invoicing"})
+public class ChargeBackTests {
+    private final static BigDecimal FIFTEEN = new BigDecimal("15.00");
+    private final static BigDecimal THIRTY = new BigDecimal("30.00");
+    private final static BigDecimal ONE_MILLION = new BigDecimal("1000000.00");
+    private InvoiceSqlDao invoiceSqlDao;
+    private InvoicePaymentApi invoicePaymentApi;
+    private CallContext context;
+    private final Clock clock = new ClockMock();
+    private final static Currency CURRENCY = Currency.EUR;
+
+    @BeforeClass
+    public void setup() {
+        MysqlTestingHelper helper = new MysqlTestingHelper();
+        IDBI dbi = helper.getDBI();
+        invoiceSqlDao = dbi.onDemand(InvoiceSqlDao.class);
+        invoiceSqlDao.test();
+
+        NextBillingDatePoster nextBillingDatePoster = new MockNextBillingDatePoster();
+        TagDao tagDao = new MockTagDao();
+        InvoiceDao invoiceDao = new DefaultInvoiceDao(dbi, nextBillingDatePoster, tagDao);
+        invoicePaymentApi = new DefaultInvoicePaymentApi(invoiceDao);
+
+        context = new TestCallContext("Charge back tests");
+    }
+
+    @Test
+    public void testCompleteChargeBack() throws InvoiceApiException {
+        Invoice invoice = createAndPersistInvoice(THIRTY);
+        InvoicePayment payment = createAndPersistPayment(invoice.getId(), THIRTY);
+
+        // create a full charge back
+        invoicePaymentApi.processChargeback(payment.getId(), THIRTY, context);
+
+        // check amount owed
+        BigDecimal amount = invoicePaymentApi.getRemainingAmountPaid(payment.getId());
+        assertTrue(amount.compareTo(BigDecimal.ZERO) == 0);
+    }
+
+    @Test
+    public void testPartialChargeBack() throws InvoiceApiException {
+        Invoice invoice = createAndPersistInvoice(THIRTY);
+        InvoicePayment payment = createAndPersistPayment(invoice.getId(), THIRTY);
+
+        // create a partial charge back
+        invoicePaymentApi.processChargeback(payment.getId(), FIFTEEN, context);
+
+        // check amount owed
+        BigDecimal amount = invoicePaymentApi.getRemainingAmountPaid(payment.getId());
+        assertTrue(amount.compareTo(FIFTEEN) == 0);
+    }
+
+    @Test(expectedExceptions = InvoiceApiException.class)
+    public void testChargeBackLargerThanPaymentAmount() throws InvoiceApiException {
+        Invoice invoice = createAndPersistInvoice(THIRTY);
+        InvoicePayment payment = createAndPersistPayment(invoice.getId(), THIRTY);
+
+        // create a large charge back
+        invoicePaymentApi.processChargeback(payment.getId(), ONE_MILLION, context);
+    }
+
+    @Test(expectedExceptions = InvoiceApiException.class)
+    public void testNegativeChargeBackAmount() throws InvoiceApiException {
+        Invoice invoice = createAndPersistInvoice(THIRTY);
+        InvoicePayment payment = createAndPersistPayment(invoice.getId(), THIRTY);
+
+        // create a partial charge back
+        invoicePaymentApi.processChargeback(payment.getId(), BigDecimal.ONE.negate(), context);
+    }
+
+    @Test
+    public void testGetAccountIdFromPaymentIdHappyPath() throws InvoiceApiException {
+        Invoice invoice = createAndPersistInvoice(THIRTY);
+        InvoicePayment payment = createAndPersistPayment(invoice.getId(), THIRTY);
+        UUID accountId = invoicePaymentApi.getAccountIdFromInvoicePaymentId(payment.getId());
+        assertEquals(accountId, invoice.getAccountId());
+    }
+
+    @Test(expectedExceptions = InvoiceApiException.class)
+    public void testGetAccountIdFromPaymentIdBadPaymentId() throws InvoiceApiException {
+        invoicePaymentApi.getAccountIdFromInvoicePaymentId(UUID.randomUUID());
+    }
+
+    @Test
+    public void testGetChargeBacksByAccountIdWithEmptyReturnSet() throws InvoiceApiException {
+        List<InvoicePayment> chargebacks = invoicePaymentApi.getChargebacksByAccountId(UUID.randomUUID());
+        assertNotNull(chargebacks);
+        assertEquals(chargebacks.size(), 0);
+    }
+
+    @Test
+    public void testGetChargeBacksByAccountIdHappyPath() throws InvoiceApiException {
+        Invoice invoice = createAndPersistInvoice(THIRTY);
+        InvoicePayment payment = createAndPersistPayment(invoice.getId(), THIRTY);
+
+        // create a partial charge back
+        invoicePaymentApi.processChargeback(payment.getId(), FIFTEEN, context);
+
+        List<InvoicePayment> chargebacks = invoicePaymentApi.getChargebacksByAccountId(invoice.getAccountId());
+        assertNotNull(chargebacks);
+        assertEquals(chargebacks.size(), 1);
+        assertEquals(chargebacks.get(0).getReversedInvoicePaymentId(), payment.getId());
+    }
+
+    @Test
+    public void testGetChargeBacksByPaymentAttemptIdWithEmptyReturnSet() throws InvoiceApiException {
+        List<InvoicePayment> chargebacks = invoicePaymentApi.getChargebacksByPaymentAttemptId(UUID.randomUUID());
+        assertNotNull(chargebacks);
+        assertEquals(chargebacks.size(), 0);
+    }
+
+    @Test
+    public void testGetChargeBacksByInvoicePaymentIdHappyPath() throws InvoiceApiException {
+        Invoice invoice = createAndPersistInvoice(THIRTY);
+        InvoicePayment payment = createAndPersistPayment(invoice.getId(), THIRTY);
+
+        // create a partial charge back
+        invoicePaymentApi.processChargeback(payment.getId(), FIFTEEN, context);
+
+        List<InvoicePayment> chargebacks = invoicePaymentApi.getChargebacksByPaymentAttemptId(payment.getPaymentAttemptId());
+        assertNotNull(chargebacks);
+        assertEquals(chargebacks.size(), 1);
+        assertEquals(chargebacks.get(0).getReversedInvoicePaymentId(), payment.getId());
+    }
+
+    private Invoice createAndPersistInvoice(BigDecimal amount) {
+        Invoice invoice = BrainDeadProxyFactory.createBrainDeadProxyFor(Invoice.class);
+        UUID invoiceId = UUID.randomUUID();
+        UUID accountId = UUID.randomUUID();
+        ZombieControl zombie = (ZombieControl) invoice;
+        zombie.addResult("getId", invoiceId);
+        zombie.addResult("getAccountId", accountId);
+        zombie.addResult("getInvoiceDate", clock.getUTCNow());
+        zombie.addResult("getTargetDate", clock.getUTCNow());
+        zombie.addResult("getCurrency", CURRENCY);
+        zombie.addResult("isMigrationInvoice", false);
+
+        List<InvoiceItem> items = new ArrayList<InvoiceItem>();
+        items.add(createInvoiceItem(invoiceId, accountId, amount));
+        zombie.addResult("getInvoiceItems", items);
+
+        invoiceSqlDao.create(invoice, context);
+
+        return invoice;
+    }
+
+    private InvoiceItem createInvoiceItem(UUID invoiceId, UUID accountId, BigDecimal amount) {
+        return new FixedPriceInvoiceItem(invoiceId, accountId, UUID.randomUUID(), UUID.randomUUID(),
+                "charge back test", "charge back phase", clock.getUTCNow(), clock.getUTCNow(), amount, CURRENCY);
+    }
+
+    private InvoicePayment createAndPersistPayment(UUID invoiceId, BigDecimal amount) {
+        InvoicePayment payment = BrainDeadProxyFactory.createBrainDeadProxyFor(InvoicePayment.class);
+        ZombieControl zombie = (ZombieControl) payment;
+        zombie.addResult("getId", UUID.randomUUID());
+        zombie.addResult("getInvoiceId", invoiceId);
+        zombie.addResult("getPaymentAttemptId", UUID.randomUUID());
+        zombie.addResult("getPaymentAttemptDate", clock.getUTCNow());
+        zombie.addResult("getAmount", amount);
+        zombie.addResult("getCurrency", CURRENCY);
+        zombie.addResult("getReversedInvoicePaymentId", BrainDeadProxyFactory.ZOMBIE_VOID);
+
+        invoicePaymentApi.notifyOfPaymentAttempt(payment, context);
+
+        return payment;
+    }
+}
diff --git a/invoice/src/test/java/com/ning/billing/invoice/tests/DefaultInvoiceGeneratorTests.java b/invoice/src/test/java/com/ning/billing/invoice/tests/DefaultInvoiceGeneratorTests.java
index 75b57ec..8f381bc 100644
--- a/invoice/src/test/java/com/ning/billing/invoice/tests/DefaultInvoiceGeneratorTests.java
+++ b/invoice/src/test/java/com/ning/billing/invoice/tests/DefaultInvoiceGeneratorTests.java
@@ -19,16 +19,18 @@ package com.ning.billing.invoice.tests;
 import static org.testng.Assert.assertEquals;
 import static org.testng.Assert.assertNotNull;
 import static org.testng.Assert.assertNull;
-import static org.testng.Assert.fail;
+import static org.testng.Assert.assertTrue;
 
 import java.math.BigDecimal;
 import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
 import java.util.List;
+import java.util.Map;
 import java.util.UUID;
 
 import javax.annotation.Nullable;
 
-import com.ning.billing.invoice.model.DefaultInvoicePayment;
 import org.joda.time.DateTime;
 import org.testng.annotations.Test;
 
@@ -47,13 +49,18 @@ import com.ning.billing.entitlement.api.SubscriptionTransitionType;
 import com.ning.billing.entitlement.api.billing.BillingEvent;
 import com.ning.billing.entitlement.api.billing.BillingModeType;
 import com.ning.billing.entitlement.api.user.Subscription;
+import com.ning.billing.invoice.MockBillingEventSet;
 import com.ning.billing.invoice.api.Invoice;
 import com.ning.billing.invoice.api.InvoiceApiException;
-import com.ning.billing.invoice.model.BillingEventSet;
+import com.ning.billing.invoice.api.InvoiceItem;
+import com.ning.billing.invoice.model.CreditInvoiceItem;
 import com.ning.billing.invoice.model.DefaultInvoiceGenerator;
+import com.ning.billing.invoice.model.DefaultInvoice;
+import com.ning.billing.invoice.model.DefaultInvoicePayment;
 import com.ning.billing.invoice.model.FixedPriceInvoiceItem;
 import com.ning.billing.invoice.model.InvoiceGenerator;
 import com.ning.billing.invoice.model.RecurringInvoiceItem;
+import com.ning.billing.junction.api.BillingEventSet;
 import com.ning.billing.mock.BrainDeadProxyFactory;
 import com.ning.billing.mock.BrainDeadProxyFactory.ZombieControl;
 import com.ning.billing.util.clock.Clock;
@@ -96,7 +103,7 @@ public class DefaultInvoiceGeneratorTests extends InvoicingTestBase {
 
     @Test
     public void testWithEmptyEventSet() throws InvoiceApiException {
-        BillingEventSet events = new BillingEventSet();
+        BillingEventSet events = new MockBillingEventSet();
 
         UUID accountId = UUID.randomUUID();
         Invoice invoice = generator.generateInvoice(accountId, events, null, new DateTime(), Currency.USD);
@@ -106,7 +113,7 @@ public class DefaultInvoiceGeneratorTests extends InvoicingTestBase {
 
     @Test
     public void testWithSingleMonthlyEvent() throws InvoiceApiException, CatalogApiException {
-        BillingEventSet events = new BillingEventSet();
+        BillingEventSet events = new MockBillingEventSet();
 
         Subscription sub = createZombieSubscription();
         DateTime startDate = buildDateTime(2011, 9, 1);
@@ -124,7 +131,7 @@ public class DefaultInvoiceGeneratorTests extends InvoicingTestBase {
 
         assertNotNull(invoice);
         assertEquals(invoice.getNumberOfItems(), 2);
-        assertEquals(invoice.getTotalAmount(), TWENTY);
+        assertEquals(invoice.getBalance(), TWENTY);
         assertEquals(invoice.getInvoiceItems().get(0).getSubscriptionId(), sub.getId());
     }
 
@@ -142,7 +149,7 @@ public class DefaultInvoiceGeneratorTests extends InvoicingTestBase {
 
     @Test
     public void testWithSingleMonthlyEventWithLeadingProRation() throws InvoiceApiException, CatalogApiException {
-        BillingEventSet events = new BillingEventSet();
+        BillingEventSet events = new MockBillingEventSet();
 
         Subscription sub = createZombieSubscription();
         DateTime startDate = buildDateTime(2011, 9, 1);
@@ -163,12 +170,12 @@ public class DefaultInvoiceGeneratorTests extends InvoicingTestBase {
         BigDecimal expectedNumberOfBillingCycles;
         expectedNumberOfBillingCycles = ONE.add(FOURTEEN.divide(THIRTY_ONE, 2 * NUMBER_OF_DECIMALS, ROUNDING_METHOD));
         BigDecimal expectedAmount = expectedNumberOfBillingCycles.multiply(rate).setScale(NUMBER_OF_DECIMALS, ROUNDING_METHOD);
-        assertEquals(invoice.getTotalAmount(), expectedAmount);
+        assertEquals(invoice.getBalance(), expectedAmount);
     }
 
     @Test
     public void testTwoMonthlySubscriptionsWithAlignedBillingDates() throws InvoiceApiException, CatalogApiException {
-        BillingEventSet events = new BillingEventSet();
+        BillingEventSet events = new MockBillingEventSet();
 
         Plan plan1 = new MockPlan();
         BigDecimal rate1 = FIVE;
@@ -192,12 +199,12 @@ public class DefaultInvoiceGeneratorTests extends InvoicingTestBase {
 
         assertNotNull(invoice);
         assertEquals(invoice.getNumberOfItems(), 2);
-        assertEquals(invoice.getTotalAmount(), rate1.add(rate2).setScale(NUMBER_OF_DECIMALS));
+        assertEquals(invoice.getBalance(), rate1.add(rate2).setScale(NUMBER_OF_DECIMALS));
     }
 
     @Test
     public void testOnePlan_TwoMonthlyPhases_ChangeImmediate() throws InvoiceApiException, CatalogApiException {
-        BillingEventSet events = new BillingEventSet();
+        BillingEventSet events = new MockBillingEventSet();
 
         Plan plan1 = new MockPlan();
         BigDecimal rate1 = FIVE;
@@ -229,12 +236,12 @@ public class DefaultInvoiceGeneratorTests extends InvoicingTestBase {
         expectedValue = expectedValue.add(numberOfCyclesEvent2.multiply(rate2));
         expectedValue = expectedValue.setScale(NUMBER_OF_DECIMALS, ROUNDING_METHOD);
 
-        assertEquals(invoice.getTotalAmount(), expectedValue);
+        assertEquals(invoice.getBalance(), expectedValue);
     }
 
     @Test
     public void testOnePlan_ThreeMonthlyPhases_ChangeEOT() throws InvoiceApiException, CatalogApiException {
-        BillingEventSet events = new BillingEventSet();
+        BillingEventSet events = new MockBillingEventSet();
 
         Plan plan1 = new MockPlan();
         BigDecimal rate1 = FIVE;
@@ -260,12 +267,12 @@ public class DefaultInvoiceGeneratorTests extends InvoicingTestBase {
 
         assertNotNull(invoice);
         assertEquals(invoice.getNumberOfItems(), 4);
-        assertEquals(invoice.getTotalAmount(), rate1.add(rate2).add(TWO.multiply(rate3)).setScale(NUMBER_OF_DECIMALS));
+        assertEquals(invoice.getBalance(), rate1.add(rate2).add(TWO.multiply(rate3)).setScale(NUMBER_OF_DECIMALS));
     }
 
     @Test
     public void testSingleEventWithExistingInvoice() throws InvoiceApiException, CatalogApiException {
-        BillingEventSet events = new BillingEventSet();
+        BillingEventSet events = new MockBillingEventSet();
 
         Subscription sub = createZombieSubscription();
         DateTime startDate = buildDateTime(2011, 9, 1);
@@ -289,6 +296,7 @@ public class DefaultInvoiceGeneratorTests extends InvoicingTestBase {
         assertNull(invoice2);
     }
 
+    // TODO: modify this test to keep a running total of expected invoice amount over time
     @Test
     public void testMultiplePlansWithUtterChaos() throws InvoiceApiException, CatalogApiException {
         // plan 1: change of phase from trial to discount followed by immediate cancellation; (covers phase change, cancel, pro-ration)
@@ -341,7 +349,7 @@ public class DefaultInvoiceGeneratorTests extends InvoicingTestBase {
 
         BigDecimal expectedAmount;
         List<Invoice> invoices = new ArrayList<Invoice>();
-        BillingEventSet events = new BillingEventSet();
+        BillingEventSet events = new MockBillingEventSet();
 
         // on 1/5/2011, create subscription 1 (trial)
         events.add(createBillingEvent(subscriptionId1, plan1StartDate, plan1, plan1Phase1, 5));
@@ -372,7 +380,8 @@ public class DefaultInvoiceGeneratorTests extends InvoicingTestBase {
 
         // on 4/29/2011, cancel subscription 1
         events.add(createBillingEvent(subscriptionId1, plan1CancelDate, plan1, plan1Phase3, 5));
-        expectedAmount = TWELVE.multiply(SIX.divide(THIRTY, NUMBER_OF_DECIMALS, ROUNDING_METHOD)).negate().setScale(NUMBER_OF_DECIMALS);
+        // previous invoices are adjusted; this is the pro-ration amount only
+        expectedAmount = TWELVE.multiply(TWENTY_FOUR.divide(THIRTY, NUMBER_OF_DECIMALS, ROUNDING_METHOD)).setScale(NUMBER_OF_DECIMALS);
         testInvoiceGeneration(accountId, events, invoices, plan1CancelDate, 2, expectedAmount);
 
         // on 5/10/2011, invoice subscription 2 (trial)
@@ -417,8 +426,7 @@ public class DefaultInvoiceGeneratorTests extends InvoicingTestBase {
 
         // on 7/31/2011, convert subscription 3 to annual
         events.add(createBillingEvent(subscriptionId3, plan3UpgradeToAnnualDate, plan3, plan3Phase2, 31));
-        expectedAmount = ONE_HUNDRED.subtract(TEN);
-        expectedAmount = expectedAmount.add(TEN.multiply(ELEVEN.divide(THIRTY_ONE, 2 * NUMBER_OF_DECIMALS, ROUNDING_METHOD)));
+        expectedAmount = ONE_HUNDRED.add(TEN.multiply(ELEVEN.divide(THIRTY_ONE, 2 * NUMBER_OF_DECIMALS, ROUNDING_METHOD)));
         expectedAmount = expectedAmount.setScale(NUMBER_OF_DECIMALS, ROUNDING_METHOD);
         testInvoiceGeneration(accountId, events, invoices, plan3UpgradeToAnnualDate, 3, expectedAmount);
 
@@ -442,7 +450,7 @@ public class DefaultInvoiceGeneratorTests extends InvoicingTestBase {
 
         // on 10/7/2011, invoice subscription 4 (plan 2), cancel subscription 5
         events.add(createBillingEvent(subscriptionId5, plan5CancelDate, plan5, plan5Phase2, 10));
-        expectedAmount = TWENTY_FOUR.add(TWENTY.multiply(THREE.divide(THIRTY)).negate().setScale(NUMBER_OF_DECIMALS));
+        expectedAmount = TWENTY_FOUR.add(TWENTY.multiply(TWENTY_SEVEN.divide(THIRTY)).setScale(NUMBER_OF_DECIMALS));
         testInvoiceGeneration(accountId, events, invoices, plan5CancelDate, 3, expectedAmount);
 
         // on 10/10/2011, invoice plan 2 (evergreen)
@@ -454,7 +462,7 @@ public class DefaultInvoiceGeneratorTests extends InvoicingTestBase {
     public void testZeroDollarEvents() throws InvoiceApiException, CatalogApiException {
         Plan plan = new MockPlan();
         PlanPhase planPhase = createMockMonthlyPlanPhase(ZERO);
-        BillingEventSet events = new BillingEventSet();
+        BillingEventSet events = new MockBillingEventSet();
         DateTime targetDate = buildDateTime(2011, 1, 1);
         events.add(createBillingEvent(UUID.randomUUID(), targetDate, plan, planPhase, 1));
 
@@ -467,7 +475,7 @@ public class DefaultInvoiceGeneratorTests extends InvoicingTestBase {
     public void testEndDateIsCorrect() throws InvoiceApiException, CatalogApiException {
         Plan plan = new MockPlan();
         PlanPhase planPhase = createMockMonthlyPlanPhase(ZERO);
-        BillingEventSet events = new BillingEventSet();
+        BillingEventSet events = new MockBillingEventSet();
         DateTime targetDate = new DateTime();
         events.add(createBillingEvent(UUID.randomUUID(), targetDate, plan, planPhase, targetDate.getDayOfMonth()));
 
@@ -492,7 +500,7 @@ public class DefaultInvoiceGeneratorTests extends InvoicingTestBase {
 
         DateTime changeDate = new DateTime("2012-04-1T00:00:00.000-08:00");
 
-        BillingEventSet events = new BillingEventSet();
+        BillingEventSet events = new MockBillingEventSet();
 
         BillingEvent event1 = createMockBillingEvent(null, subscription, new DateTime("2012-01-1T00:00:00.000-08:00"),
                 plan, phase1,
@@ -529,7 +537,7 @@ public class DefaultInvoiceGeneratorTests extends InvoicingTestBase {
         BigDecimal fixedCost = TEN;
         PlanPhase phase1 = createMockMonthlyPlanPhase(monthlyRate, fixedCost, PhaseType.TRIAL);
 
-        BillingEventSet events = new BillingEventSet();
+        BillingEventSet events = new MockBillingEventSet();
         UUID subscriptionId = UUID.randomUUID();
         UUID accountId = UUID.randomUUID();
 
@@ -541,7 +549,7 @@ public class DefaultInvoiceGeneratorTests extends InvoicingTestBase {
         Invoice invoice1 = generator.generateInvoice(accountId, events, null, startDate, Currency.USD);
         assertNotNull(invoice1);
         assertEquals(invoice1.getNumberOfItems(), 2);
-        assertEquals(invoice1.getTotalAmount(), FIFTEEN);
+        assertEquals(invoice1.getBalance(), FIFTEEN);
 
         List<Invoice> invoiceList = new ArrayList<Invoice>();
         invoiceList.add(invoice1);
@@ -553,7 +561,7 @@ public class DefaultInvoiceGeneratorTests extends InvoicingTestBase {
         Invoice invoice2 = generator.generateInvoice(accountId, events, invoiceList, currentDate, Currency.USD);
         assertNotNull(invoice2);
         assertEquals(invoice2.getNumberOfItems(), 1);
-        assertEquals(invoice2.getTotalAmount(), FIVE);
+        assertEquals(invoice2.getBalance(), FIVE);
     }
 
     @Test
@@ -565,7 +573,7 @@ public class DefaultInvoiceGeneratorTests extends InvoicingTestBase {
         PlanPhase phase1 = createMockMonthlyPlanPhase(null, fixedCost1, PhaseType.TRIAL);
         PlanPhase phase2 = createMockMonthlyPlanPhase(null, fixedCost2, PhaseType.EVERGREEN);
 
-        BillingEventSet events = new BillingEventSet();
+        BillingEventSet events = new MockBillingEventSet();
         UUID subscriptionId = UUID.randomUUID();
         UUID accountId = UUID.randomUUID();
 
@@ -577,7 +585,7 @@ public class DefaultInvoiceGeneratorTests extends InvoicingTestBase {
         Invoice invoice1 = generator.generateInvoice(accountId, events, null, startDate, Currency.USD);
         assertNotNull(invoice1);
         assertEquals(invoice1.getNumberOfItems(), 1);
-        assertEquals(invoice1.getTotalAmount(), fixedCost1);
+        assertEquals(invoice1.getBalance(), fixedCost1);
 
         List<Invoice> invoiceList = new ArrayList<Invoice>();
         invoiceList.add(invoice1);
@@ -591,12 +599,12 @@ public class DefaultInvoiceGeneratorTests extends InvoicingTestBase {
         Invoice invoice2 = generator.generateInvoice(accountId, events, invoiceList, phaseChangeDate, Currency.USD);
         assertNotNull(invoice2);
         assertEquals(invoice2.getNumberOfItems(), 1);
-        assertEquals(invoice2.getTotalAmount(), fixedCost2);
+        assertEquals(invoice2.getBalance(), fixedCost2);
     }
 
     @Test
-    public void testNutsFailure() throws InvoiceApiException, CatalogApiException {
-        BillingEventSet events = new BillingEventSet();
+    public void testInvoiceGenerationFailureScenario() throws InvoiceApiException, CatalogApiException {
+        BillingEventSet events = new MockBillingEventSet();
         UUID subscriptionId = UUID.randomUUID();
         UUID accountId = UUID.randomUUID();
         final int BILL_CYCLE_DAY = 15;
@@ -623,7 +631,7 @@ public class DefaultInvoiceGeneratorTests extends InvoicingTestBase {
         Invoice invoice1 = generator.generateInvoice(accountId, events, null, creationDate, Currency.USD);
         assertNotNull(invoice1);
         assertEquals(invoice1.getNumberOfItems(), 1);
-        assertEquals(invoice1.getTotalAmount().compareTo(ZERO), 0);
+        assertEquals(invoice1.getBalance().compareTo(ZERO), 0);
 
         List<Invoice> invoiceList = new ArrayList<Invoice>();
         invoiceList.add(invoice1);
@@ -632,7 +640,7 @@ public class DefaultInvoiceGeneratorTests extends InvoicingTestBase {
         assertNotNull(invoice2);
         assertEquals(invoice2.getNumberOfItems(), 1);
         assertEquals(invoice2.getInvoiceItems().get(0).getStartDate().compareTo(trialPhaseEndDate), 0);
-        assertEquals(invoice2.getTotalAmount().compareTo(new BigDecimal("3.21")), 0);
+        assertEquals(invoice2.getBalance().compareTo(new BigDecimal("3.21")), 0);
 
         invoiceList.add(invoice2);
         DateTime targetDate = trialPhaseEndDate.toMutableDateTime().dayOfMonth().set(BILL_CYCLE_DAY).toDateTime();
@@ -640,7 +648,7 @@ public class DefaultInvoiceGeneratorTests extends InvoicingTestBase {
         assertNotNull(invoice3);
         assertEquals(invoice3.getNumberOfItems(), 1);
         assertEquals(invoice3.getInvoiceItems().get(0).getStartDate().compareTo(targetDate), 0);
-        assertEquals(invoice3.getTotalAmount().compareTo(DISCOUNT_PRICE), 0);
+        assertEquals(invoice3.getBalance().compareTo(DISCOUNT_PRICE), 0);
 
         invoiceList.add(invoice3);
         targetDate = targetDate.plusMonths(6);
@@ -652,7 +660,7 @@ public class DefaultInvoiceGeneratorTests extends InvoicingTestBase {
     @Test(expectedExceptions = {InvoiceApiException.class})
     public void testTargetDateRestrictionFailure() throws InvoiceApiException, CatalogApiException {
         DateTime targetDate = DateTime.now().plusMonths(60);
-        BillingEventSet events = new BillingEventSet();
+        BillingEventSet events = new MockBillingEventSet();
         Plan plan1 = new MockPlan();
         PlanPhase phase1 = createMockMonthlyPlanPhase(null, ZERO, PhaseType.TRIAL);
         events.add(createBillingEvent(UUID.randomUUID(), DateTime.now(), plan1, phase1, 1));
@@ -706,9 +714,10 @@ public class DefaultInvoiceGeneratorTests extends InvoicingTestBase {
         Invoice invoice = generator.generateInvoice(accountId, events, existingInvoices, targetDate, currency);
         assertNotNull(invoice);
         assertEquals(invoice.getNumberOfItems(), expectedNumberOfItems);
-
         existingInvoices.add(invoice);
-        assertEquals(invoice.getTotalAmount(), expectedAmount);
+
+        distributeItems(existingInvoices);
+        assertEquals(invoice.getBalance(), expectedAmount);
     }
 
     @Test
@@ -725,14 +734,14 @@ public class DefaultInvoiceGeneratorTests extends InvoicingTestBase {
         MockInternationalPrice price20 = new MockInternationalPrice(new DefaultPrice(TWENTY, Currency.USD));
         PlanPhase basePlanEvergreen = new MockPlanPhase(price10, null, BillingPeriod.MONTHLY, PhaseType.EVERGREEN);
 
-        BillingEventSet events = new BillingEventSet();
+        BillingEventSet events = new MockBillingEventSet();
         events.add(createBillingEvent(baseSubscription.getId(), april25, basePlan, basePlanEvergreen, 25));
 
         // generate invoice
         Invoice invoice1 = generator.generateInvoice(accountId, events, null, april25, Currency.USD);
         assertNotNull(invoice1);
         assertEquals(invoice1.getNumberOfItems(), 1);
-        assertEquals(invoice1.getTotalAmount().compareTo(TEN), 0);
+        assertEquals(invoice1.getBalance().compareTo(TEN), 0);
 
         List<Invoice> invoices = new ArrayList<Invoice>();
         invoices.add(invoice1);
@@ -754,11 +763,11 @@ public class DefaultInvoiceGeneratorTests extends InvoicingTestBase {
         invoices.add(invoice2);
         assertNotNull(invoice2);
         assertEquals(invoice2.getNumberOfItems(), 2);
-        assertEquals(invoice2.getTotalAmount().compareTo(TWENTY_FIVE.multiply(new BigDecimal("0.9")).setScale(NUMBER_OF_DECIMALS, ROUNDING_METHOD)), 0);
+        assertEquals(invoice2.getBalance().compareTo(TWENTY_FIVE.multiply(new BigDecimal("0.9")).setScale(NUMBER_OF_DECIMALS, ROUNDING_METHOD)), 0);
 
         // perform a repair (change base plan; remove one add-on)
         // event stream should include just two plans
-        BillingEventSet newEvents = new BillingEventSet();
+        MockBillingEventSet newEvents = new MockBillingEventSet();
         Plan basePlan2 = new MockPlan("base plan 2");
         MockInternationalPrice price13 = new MockInternationalPrice(new DefaultPrice(THIRTEEN, Currency.USD));
         PlanPhase basePlan2Phase = new MockPlanPhase(price13, null, BillingPeriod.MONTHLY, PhaseType.EVERGREEN);
@@ -771,7 +780,7 @@ public class DefaultInvoiceGeneratorTests extends InvoicingTestBase {
         assertNotNull(invoice3);
         assertEquals(invoice3.getNumberOfItems(), 5);
         // -4.50 -18 - 10 (to correct the previous 2 invoices) + 4.50 + 13
-        assertEquals(invoice3.getTotalAmount().compareTo(FIFTEEN.negate()), 0);
+        assertEquals(invoice3.getBalance().compareTo(FIFTEEN.negate()), 0);
     }
 
     @Test
@@ -787,10 +796,11 @@ public class DefaultInvoiceGeneratorTests extends InvoicingTestBase {
         MockInternationalPrice price10 = new MockInternationalPrice(new DefaultPrice(TEN, Currency.USD));
         PlanPhase originalPlanEvergreen = new MockPlanPhase(price10, null, BillingPeriod.MONTHLY, PhaseType.EVERGREEN);
 
-        BillingEventSet events = new BillingEventSet();
+        BillingEventSet events = new MockBillingEventSet();
         events.add(createBillingEvent(originalSubscription.getId(), april25, originalPlan, originalPlanEvergreen, 25));
 
         Invoice invoice1 = generator.generateInvoice(accountId, events, null, april25, Currency.USD);
+        assertEquals(invoice1.getNumberOfItems(), 1);
         List<Invoice> invoices = new ArrayList<Invoice>();
         invoices.add(invoice1);
 
@@ -808,41 +818,120 @@ public class DefaultInvoiceGeneratorTests extends InvoicingTestBase {
 
         // generate a new invoice
         Invoice invoice2 = generator.generateInvoice(accountId, events, invoices, april25, Currency.USD);
+        assertEquals(invoice2.getNumberOfItems(), 4);
         invoices.add(invoice2);
 
         // move items to the correct invoice (normally, the dao calls will sort that out)
-        generator.distributeItems(invoices);
+        distributeItems(invoices);
 
         // ensure that the original invoice balance is zero
-
         assertEquals(invoice1.getBalance().compareTo(ZERO), 0);
 
         // ensure that the account balance is correct
-        assertEquals(invoice2.getBalance().compareTo(FIVE.negate()), 0);
+        assertEquals(invoice2.getBalance().compareTo(ZERO), 0);
 
+        // ensure that the account has a credit balance
+        BigDecimal creditBalance = invoice1.getAmountCredited().add(invoice2.getAmountCredited());
+        assertTrue(creditBalance.compareTo(FIVE) == 0);
     }
 
-    /*
-    test scenario: create invoice, pay invoice, change entitlement (repair)
+    private void distributeItems(List<Invoice> invoices) {
+        Map<UUID, Invoice> invoiceMap = new HashMap<UUID, Invoice>();
 
-    test scenario: two subscriptions, one with auto_invoice_off
+        for (Invoice invoice : invoices) {
+            invoiceMap.put(invoice.getId(), invoice);
+        }
 
-    test scenario: create invoice, pay invoice, change entitlement, generate invoices to use up credits
+        for (final Invoice invoice: invoices) {
+            Iterator<InvoiceItem> itemIterator = invoice.getInvoiceItems().iterator();
+            final UUID invoiceId = invoice.getId();
 
-    test scenario: create invoice, pay invoice, add account-level credit, generate invoice
+            while (itemIterator.hasNext()) {
+                InvoiceItem item = itemIterator.next();
 
-
-     */
+                if (!item.getInvoiceId().equals(invoiceId)) {
+                    Invoice thisInvoice = invoiceMap.get(item.getInvoiceId());
+                    if (thisInvoice == null) {
+                        throw new NullPointerException();
+                    }
+                    thisInvoice.addInvoiceItem(item);
+                    itemIterator.remove();
+                }
+            }
+        }
+    }
 
     @Test
-    public void testAutoInvoiceOff() {
-        BillingEventSet eventSet = new BillingEventSet();
-        fail();
+    public void testAutoInvoiceOffAccount() throws Exception {
+        MockBillingEventSet events = new MockBillingEventSet();
+        events.setAccountInvoiceOff(true);
+
+        Subscription sub = createZombieSubscription();
+        DateTime startDate = buildDateTime(2011, 9, 1);
+
+        Plan plan = new MockPlan();
+        BigDecimal rate1 = TEN;
+        PlanPhase phase = createMockMonthlyPlanPhase(rate1);
+
+        BillingEvent event = createBillingEvent(sub.getId(), startDate, plan, phase, 1);
+        events.add(event);
+
+        DateTime targetDate = buildDateTime(2011, 10, 3);
+        UUID accountId = UUID.randomUUID();
+        Invoice invoice = generator.generateInvoice(accountId, events, null, targetDate, Currency.USD);
+
+        assertNull(invoice);
     }
+    
+    public void testAutoInvoiceOffWithCredits() throws CatalogApiException, InvoiceApiException {
+        Currency currency = Currency.USD;
+        List<Invoice> invoices = new ArrayList<Invoice>();
+        MockBillingEventSet eventSet = new MockBillingEventSet();
+        UUID accountId = UUID.randomUUID();
+
+        DateTime startDate = new DateTime(2012, 1, 1, 0, 12, 34, 0);
+
+        // add first subscription creation event
+        UUID subscriptionId1 = UUID.randomUUID();
+        Plan plan1 = new MockPlan();
+        PlanPhase plan1phase1 = createMockMonthlyPlanPhase(FIFTEEN, null, PhaseType.DISCOUNT);
+        BillingEvent subscription1creation = createBillingEvent(subscriptionId1, startDate, plan1, plan1phase1, 1);
+        eventSet.add(subscription1creation);
+
+        // add second subscription creation event
+        UUID subscriptionId2 = UUID.randomUUID();
+        Plan plan2 = new MockPlan();
+        PlanPhase plan2phase1 = createMockMonthlyPlanPhase(TWELVE, null, PhaseType.EVERGREEN);
+        eventSet.add(createBillingEvent(subscriptionId2, startDate, plan2, plan2phase1, 1));
+
+        // generate the first invoice
+        Invoice invoice1 = generator.generateInvoice(accountId, eventSet, invoices, startDate, currency);
+        assertNotNull(invoice1);
+        assertTrue(invoice1.getBalance().compareTo(FIFTEEN.add(TWELVE)) == 0);
+        invoices.add(invoice1);
 
-    @Test(enabled = false)
+        // set auto invoice off for first subscription (i.e. remove event from BillingEventSet and add subscription id to the list
+        // generate invoice
+        eventSet.remove(subscription1creation);
+        eventSet.addSubscriptionWithAutoInvoiceOff(subscriptionId1);
+
+        DateTime targetDate2 = startDate.plusMonths(1);
+        Invoice invoice2 = generator.generateInvoice(accountId, eventSet, invoices, targetDate2, currency);
+        assertNotNull(invoice2);
+        assertTrue(invoice2.getBalance().compareTo(TWELVE) == 0);
+        invoices.add(invoice2);
+
+        DateTime targetDate3 = targetDate2.plusMonths(1);
+        eventSet.clearSubscriptionsWithAutoInvoiceOff();
+        eventSet.add(subscription1creation);
+        Invoice invoice3 = generator.generateInvoice(accountId, eventSet, invoices, targetDate3, currency);
+        assertNotNull(invoice3);
+        assertTrue(invoice3.getBalance().compareTo(FIFTEEN.multiply(TWO).add(TWELVE)) == 0);
+    }
+
+    @Test
     public void testAccountCredit() throws CatalogApiException, InvoiceApiException {
-        BillingEventSet billingEventSet = new BillingEventSet();
+        BillingEventSet billingEventSet = new MockBillingEventSet();
 
         DateTime startDate = new DateTime(2012, 3, 1, 0, 0, 0, 0);
         UUID accountId = UUID.randomUUID();
@@ -853,9 +942,24 @@ public class DefaultInvoiceGeneratorTests extends InvoicingTestBase {
         BillingEvent creation = createBillingEvent(subscriptionId, startDate, plan, planPhase, 1);
         billingEventSet.add(creation);
 
-        Invoice invoice = generator.generateInvoice(accountId, billingEventSet, null, startDate, Currency.USD);
-        assertNotNull(invoice);
-        assertEquals(invoice.getNumberOfItems(), 1);
-        assertEquals(invoice.getTotalAmount().compareTo(TEN), 0);
+        List<Invoice> invoices = new ArrayList<Invoice>();
+
+        Invoice initialInvoice = generator.generateInvoice(accountId, billingEventSet, null, startDate, Currency.USD);
+        assertNotNull(initialInvoice);
+        assertEquals(initialInvoice.getNumberOfItems(), 1);
+        assertEquals(initialInvoice.getBalance().compareTo(TEN), 0);
+        invoices.add(initialInvoice);
+
+        // add account-level credit
+        DateTime creditDate = startDate.plusDays(5);
+        Invoice invoiceWithCredit = new DefaultInvoice(accountId, creditDate, creditDate, Currency.USD);
+        InvoiceItem accountCredit = new CreditInvoiceItem(invoiceWithCredit.getId(), accountId, creditDate, FIVE, Currency.USD);
+        invoiceWithCredit.addInvoiceItem(accountCredit);
+        invoices.add(invoiceWithCredit);
+
+        // invoice one month after the initial subscription
+        Invoice finalInvoice = generator.generateInvoice(accountId, billingEventSet, invoices, startDate.plusMonths(1), Currency.USD);
+        assertEquals(finalInvoice.getBalance().compareTo(FIVE), 0);
+        assertEquals(finalInvoice.getNumberOfItems(), 2);
     }
 }
\ No newline at end of file
diff --git a/invoice/src/test/java/com/ning/billing/invoice/tests/InvoicingTestBase.java b/invoice/src/test/java/com/ning/billing/invoice/tests/InvoicingTestBase.java
index 25be5e2..5beea5c 100644
--- a/invoice/src/test/java/com/ning/billing/invoice/tests/InvoicingTestBase.java
+++ b/invoice/src/test/java/com/ning/billing/invoice/tests/InvoicingTestBase.java
@@ -62,6 +62,7 @@ public abstract class InvoicingTestBase {
     protected static final BigDecimal TWENTY_FOUR = new BigDecimal("24.0").setScale(NUMBER_OF_DECIMALS);
     protected static final BigDecimal TWENTY_FIVE = new BigDecimal("25.0").setScale(NUMBER_OF_DECIMALS);
 
+    protected static final BigDecimal TWENTY_SEVEN = new BigDecimal("27.0").setScale(NUMBER_OF_DECIMALS);
     protected static final BigDecimal TWENTY_EIGHT = new BigDecimal("28.0").setScale(NUMBER_OF_DECIMALS);
     protected static final BigDecimal TWENTY_NINE = new BigDecimal("29.0").setScale(NUMBER_OF_DECIMALS);
     protected static final BigDecimal THIRTY = new BigDecimal("30.0").setScale(NUMBER_OF_DECIMALS);
diff --git a/jaxrs/src/main/java/com/ning/billing/jaxrs/json/AccountJson.java b/jaxrs/src/main/java/com/ning/billing/jaxrs/json/AccountJson.java
index ae9220f..2d0eab4 100644
--- a/jaxrs/src/main/java/com/ning/billing/jaxrs/json/AccountJson.java
+++ b/jaxrs/src/main/java/com/ning/billing/jaxrs/json/AccountJson.java
@@ -181,7 +181,7 @@ public class AccountJson extends AccountJsonSimple {
     public AccountJson(@JsonProperty("accountId") String accountId,
             @JsonProperty("name") String name,
             @JsonProperty("firstNameLength") Integer length,
-            @JsonProperty("external_key") String externalKey,
+            @JsonProperty("externalKey") String externalKey,
             @JsonProperty("email") String email,
             @JsonProperty("billingDay") Integer billCycleDay,
             @JsonProperty("currency") String currency,
diff --git a/jaxrs/src/main/java/com/ning/billing/jaxrs/json/AccountTimelineJson.java b/jaxrs/src/main/java/com/ning/billing/jaxrs/json/AccountTimelineJson.java
index 2b573c1..0afc104 100644
--- a/jaxrs/src/main/java/com/ning/billing/jaxrs/json/AccountTimelineJson.java
+++ b/jaxrs/src/main/java/com/ning/billing/jaxrs/json/AccountTimelineJson.java
@@ -1,4 +1,4 @@
-/* 
+/*
  * Copyright 2010-2011 Ning, Inc.
  *
  * Ning licenses this file to you under the Apache License, version 2.0
@@ -25,7 +25,6 @@ import java.util.UUID;
 import com.fasterxml.jackson.annotation.JsonCreator;
 import com.fasterxml.jackson.annotation.JsonProperty;
 import com.fasterxml.jackson.annotation.JsonView;
-
 import com.ning.billing.account.api.Account;
 import com.ning.billing.entitlement.api.timeline.BundleTimeline;
 import com.ning.billing.invoice.api.Invoice;
@@ -40,20 +39,21 @@ public class AccountTimelineJson {
     private final List<InvoiceJsonWithBundleKeys> invoices;
     
     private final AccountJsonSimple account;
-    
+
+
     private final List<BundleJsonWithSubscriptions> bundles;
-    
+
     @JsonCreator
     public AccountTimelineJson(@JsonProperty("account") AccountJsonSimple account,
             @JsonProperty("bundles") List<BundleJsonWithSubscriptions> bundles,
-            @JsonProperty("invoices") List<InvoiceJsonWithBundleKeys> invoices,            
+            @JsonProperty("invoices") List<InvoiceJsonWithBundleKeys> invoices,
             @JsonProperty("payments") List<PaymentJsonWithBundleKeys> payments) {
         this.account = account;
         this.bundles = bundles;
         this.invoices = invoices;
         this.payments = payments;
     }
-    
+
     private String getBundleExternalKey(UUID invoiceId,  List<Invoice> invoices, List<BundleTimeline> bundles) {
         for (Invoice cur : invoices) {
             if (cur.getId().equals(invoiceId)) {
@@ -62,7 +62,7 @@ public class AccountTimelineJson {
         }
         return null;
     }
-    
+
     private String getBundleExternalKey(Invoice invoice, List<BundleTimeline> bundles) {
         Set<UUID> b = new HashSet<UUID>();
         for (final InvoiceItem cur : invoice.getInvoiceItems()) {
@@ -84,21 +84,26 @@ public class AccountTimelineJson {
         }
         return tmp.toString();
     }
-    
+
     public AccountTimelineJson(Account account, List<Invoice> invoices, List<Payment> payments, List<BundleTimeline> bundles) {
         this.account = new AccountJsonSimple(account.getId().toString(), account.getExternalKey());
         this.bundles = new LinkedList<BundleJsonWithSubscriptions>();
         for (BundleTimeline cur : bundles) {
-            this.bundles.add(new BundleJsonWithSubscriptions(account.getId(), cur));            
+            this.bundles.add(new BundleJsonWithSubscriptions(account.getId(), cur));
         }
         this.invoices = new LinkedList<InvoiceJsonWithBundleKeys>();
         for (Invoice cur : invoices) {
-            this.invoices.add(new InvoiceJsonWithBundleKeys(cur.getTotalAmount(), cur.getId().toString(), cur.getInvoiceDate(), cur.getTargetDate(),
-                    Integer.toString(cur.getInvoiceNumber()), cur.getBalance(),
-                    getBundleExternalKey(cur, bundles)));
+            this.invoices.add(new InvoiceJsonWithBundleKeys(cur.getAmountPaid(),
+                                                            cur.getAmountCredited(),
+                                                            cur.getId().toString(),
+                                                            cur.getInvoiceDate(),
+                                                            cur.getTargetDate(),
+                                                            Integer.toString(cur.getInvoiceNumber()),
+                                                            cur.getBalance(),
+                                                            cur.getAccountId().toString(),
+                                                            getBundleExternalKey(cur, bundles)));
         }
         this.payments = new LinkedList<PaymentJsonWithBundleKeys>();
-
         for (Payment cur : payments) {
         
             String status = cur.getPaymentStatus().toString();
@@ -110,7 +115,7 @@ public class AccountTimelineJson {
                     getBundleExternalKey(cur.getInvoiceId(), invoices, bundles)));
           }
     }
-    
+
     public AccountTimelineJson() {
         this.account = null;
         this.bundles = null;
diff --git a/jaxrs/src/main/java/com/ning/billing/jaxrs/json/ChargebackCollectionJson.java b/jaxrs/src/main/java/com/ning/billing/jaxrs/json/ChargebackCollectionJson.java
new file mode 100644
index 0000000..3da8e81
--- /dev/null
+++ b/jaxrs/src/main/java/com/ning/billing/jaxrs/json/ChargebackCollectionJson.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2010-2011 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.
+ */
+
+package com.ning.billing.jaxrs.json;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+import java.util.List;
+
+public class ChargebackCollectionJson {
+    private final String accountId;
+    private final List<ChargebackJson> chargebacks;
+
+    @JsonCreator
+    public ChargebackCollectionJson(@JsonProperty("accountId") final String accountId,
+                                    @JsonProperty("chargebacks") final List<ChargebackJson> chargebacks) {
+        this.accountId = accountId;
+        this.chargebacks = chargebacks;
+    }
+
+    public String getAccountId() {
+        return accountId;
+    }
+
+    public List<ChargebackJson> getChargebacks() {
+        return chargebacks;
+    }
+}
diff --git a/jaxrs/src/main/java/com/ning/billing/jaxrs/json/ChargebackJson.java b/jaxrs/src/main/java/com/ning/billing/jaxrs/json/ChargebackJson.java
new file mode 100644
index 0000000..071f971
--- /dev/null
+++ b/jaxrs/src/main/java/com/ning/billing/jaxrs/json/ChargebackJson.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright 2010-2011 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.
+ */
+
+package com.ning.billing.jaxrs.json;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.ning.billing.invoice.api.InvoicePayment;
+import org.joda.time.DateTime;
+
+import java.math.BigDecimal;
+
+// TODO: populate reason code, requested date from audit log
+public class ChargebackJson {
+    private final DateTime requestedDate;
+    private final DateTime effectiveDate;
+    private final BigDecimal chargebackAmount;
+    private final String paymentId;
+    private final String reason;
+
+    @JsonCreator
+    public ChargebackJson(@JsonProperty("requestedDate") DateTime requestedDate,
+                          @JsonProperty("effectiveDate") DateTime effectiveDate,
+                          @JsonProperty("chargebackAmount") BigDecimal chargebackAmount,
+                          @JsonProperty("paymentId") String paymentId,
+                          @JsonProperty("reason") String reason) {
+        this.requestedDate = requestedDate;
+        this.effectiveDate = effectiveDate;
+        this.chargebackAmount = chargebackAmount;
+        this.paymentId = paymentId;
+        this.reason = reason;
+    }
+
+    public ChargebackJson(InvoicePayment chargeback) {
+        this.requestedDate = null;
+        this.effectiveDate = chargeback.getPaymentAttemptDate();
+        this.chargebackAmount = chargeback.getAmount().negate();
+        this.paymentId = chargeback.getReversedInvoicePaymentId().toString();
+        this.reason = null;
+
+    }
+
+    public DateTime getRequestedDate() {
+        return requestedDate;
+    }
+
+    public DateTime getEffectiveDate() {
+        return effectiveDate;
+    }
+
+    public BigDecimal getChargebackAmount() {
+        return chargebackAmount;
+    }
+
+    public String getPaymentId() {
+        return paymentId;
+    }
+
+    public String getReason() {
+        return reason;
+    }
+}
diff --git a/jaxrs/src/main/java/com/ning/billing/jaxrs/json/CreditCollectionJson.java b/jaxrs/src/main/java/com/ning/billing/jaxrs/json/CreditCollectionJson.java
new file mode 100644
index 0000000..66f4d0c
--- /dev/null
+++ b/jaxrs/src/main/java/com/ning/billing/jaxrs/json/CreditCollectionJson.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2010-2011 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.
+ */
+
+package com.ning.billing.jaxrs.json;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+import java.util.List;
+import java.util.UUID;
+
+public class CreditCollectionJson {
+    private final UUID accountId;
+    private final List<CreditJson> credits;
+
+    @JsonCreator
+    public CreditCollectionJson(@JsonProperty("accountId") final UUID accountId,
+                                @JsonProperty("credits") final List<CreditJson> credits) {
+        this.accountId = accountId;
+        this.credits = credits;
+    }
+
+    public UUID getAccountId() {
+        return accountId;
+    }
+
+    public List<CreditJson> getCredits() {
+        return credits;
+    }
+}
diff --git a/jaxrs/src/main/java/com/ning/billing/jaxrs/json/CreditJson.java b/jaxrs/src/main/java/com/ning/billing/jaxrs/json/CreditJson.java
new file mode 100644
index 0000000..bea3cf4
--- /dev/null
+++ b/jaxrs/src/main/java/com/ning/billing/jaxrs/json/CreditJson.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright 2010-2011 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.
+ */
+
+package com.ning.billing.jaxrs.json;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.ning.billing.invoice.api.InvoiceItem;
+import org.joda.time.DateTime;
+
+import java.math.BigDecimal;
+import java.util.UUID;
+
+// TODO: add invoice number, reason and requested date to the json
+public class CreditJson {
+    private final BigDecimal creditAmount;
+    private final UUID invoiceId;
+    private final String invoiceNumber;
+    private final DateTime requestedDate;
+    private final DateTime effectiveDate;
+    private final String reason;
+
+    @JsonCreator
+    public CreditJson(@JsonProperty("creditAmount") final BigDecimal creditAmount,
+                      @JsonProperty("invoiceId") final UUID invoiceId,
+                      @JsonProperty("invoiceNumber") final String invoiceNumber,
+                      @JsonProperty("requestedDate") final DateTime requestedDate,
+                      @JsonProperty("effectiveDate") final DateTime effectiveDate,
+                      @JsonProperty("reason") final String reason) {
+        this.creditAmount = creditAmount;
+        this.invoiceId = invoiceId;
+        this.invoiceNumber = invoiceNumber;
+        this.requestedDate = requestedDate;
+        this.effectiveDate = effectiveDate;
+        this.reason = reason;
+    }
+
+    public CreditJson(InvoiceItem credit) {
+        this.creditAmount = credit.getAmount();
+        this.invoiceId = credit.getInvoiceId();
+        this.invoiceNumber = null;
+        this.requestedDate = null;
+        this.effectiveDate = credit.getStartDate();
+        this.reason = null;
+    }
+
+    public BigDecimal getCreditAmount() {
+        return creditAmount;
+    }
+
+    public UUID getInvoiceId() {
+        return invoiceId;
+    }
+
+    public String getInvoiceNumber() {
+        return invoiceNumber;
+    }
+
+    public DateTime getRequestedDate() {
+        return requestedDate;
+    }
+
+    public DateTime getEffectiveDate() {
+        return effectiveDate;
+    }
+
+    public String getReason() {
+        return reason;
+    }
+}
diff --git a/jaxrs/src/main/java/com/ning/billing/jaxrs/json/InvoiceItemJsonSimple.java b/jaxrs/src/main/java/com/ning/billing/jaxrs/json/InvoiceItemJsonSimple.java
new file mode 100644
index 0000000..e3011f3
--- /dev/null
+++ b/jaxrs/src/main/java/com/ning/billing/jaxrs/json/InvoiceItemJsonSimple.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright 2010-2011 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.
+ */
+
+package com.ning.billing.jaxrs.json;
+
+import java.math.BigDecimal;
+import java.util.UUID;
+
+import org.joda.time.DateTime;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.ning.billing.catalog.api.Currency;
+import com.ning.billing.invoice.api.InvoiceItem;
+
+public class InvoiceItemJsonSimple {
+    private final UUID invoiceId;
+    private final UUID accountId;
+    private final UUID bundleId;
+    private final UUID subscriptionId;
+    private final String planName;
+    private final String phaseName;
+    private final String description;
+    private final DateTime startDate;
+    private final DateTime endDate;
+    private final BigDecimal amount;
+    private final Currency currency;
+
+    public InvoiceItemJsonSimple(@JsonProperty("invoiceId") UUID invoiceId,
+                                 @JsonProperty("accountId") UUID accountId,
+                                 @JsonProperty("bundleId") UUID bundleId,
+                                 @JsonProperty("subscriptionId") UUID subscriptionId,
+                                 @JsonProperty("planName") String planName,
+                                 @JsonProperty("phaseName") String phaseName,
+                                 @JsonProperty("description") String description,
+                                 @JsonProperty("startDate") DateTime startDate,
+                                 @JsonProperty("endDate") DateTime endDate,
+                                 @JsonProperty("amount") BigDecimal amount,
+                                 @JsonProperty("currency") Currency currency) {
+        this.invoiceId = invoiceId;
+        this.accountId = accountId;
+        this.bundleId = bundleId;
+        this.subscriptionId = subscriptionId;
+        this.planName = planName;
+        this.phaseName = phaseName;
+        this.description = description;
+        this.startDate = startDate;
+        this.endDate = endDate;
+        this.amount = amount;
+        this.currency = currency;
+    }
+
+    public InvoiceItemJsonSimple(InvoiceItem item) {
+        this.invoiceId = item.getInvoiceId();
+        this.accountId = item.getAccountId();
+        this.bundleId = item.getBundleId();
+        this.subscriptionId = item.getSubscriptionId();
+        this.planName = item.getPlanName();
+        this.phaseName = item.getPhaseName();
+        this.description = item.getDescription();
+        this.startDate = item.getStartDate();
+        this.endDate = item.getEndDate();
+        this.amount = item.getAmount();
+        this.currency = item.getCurrency();
+    }
+
+    public UUID getInvoiceId() {
+        return invoiceId;
+    }
+
+    public UUID getAccountId() {
+        return accountId;
+    }
+
+    public UUID getBundleId() {
+        return bundleId;
+    }
+
+    public UUID getSubscriptionId() {
+        return subscriptionId;
+    }
+
+    public String getPlanName() {
+        return planName;
+    }
+
+    public String getPhaseName() {
+        return phaseName;
+    }
+
+    public String getDescription() {
+        return description;
+    }
+
+    public DateTime getStartDate() {
+        return startDate;
+    }
+
+    public DateTime getEndDate() {
+        return endDate;
+    }
+
+    public BigDecimal getAmount() {
+        return amount;
+    }
+
+    public Currency getCurrency() {
+        return currency;
+    }
+}
diff --git a/jaxrs/src/main/java/com/ning/billing/jaxrs/json/InvoiceJsonSimple.java b/jaxrs/src/main/java/com/ning/billing/jaxrs/json/InvoiceJsonSimple.java
index 1f4100b..0921030 100644
--- a/jaxrs/src/main/java/com/ning/billing/jaxrs/json/InvoiceJsonSimple.java
+++ b/jaxrs/src/main/java/com/ning/billing/jaxrs/json/InvoiceJsonSimple.java
@@ -18,66 +18,73 @@ package com.ning.billing.jaxrs.json;
 
 import java.math.BigDecimal;
 
-import com.fasterxml.jackson.annotation.JsonCreator;
-import com.fasterxml.jackson.annotation.JsonProperty;
-import com.fasterxml.jackson.annotation.JsonView;
 import org.joda.time.DateTime;
 
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonProperty;
 import com.ning.billing.invoice.api.Invoice;
 
 public class InvoiceJsonSimple {
 
     private final BigDecimal amount;
-    
     private final String invoiceId;
-    
     private final DateTime invoiceDate;
-
     private final DateTime targetDate;
-
     private final String invoiceNumber;
-    
+    private final BigDecimal credit;    
     private final BigDecimal balance;
+    private final String accountId;
 
-    
     public InvoiceJsonSimple() {
-        this.amount = null;
+        this.amount = BigDecimal.ZERO;
+        this.credit = BigDecimal.ZERO;
         this.invoiceId = null;
         this.invoiceDate = null;
-        this.targetDate = null;        
+        this.targetDate = null;
         this.invoiceNumber = null;
-        this.balance = null;
+        this.balance = BigDecimal.ZERO;
+        this.accountId = null;
     }
-    
+
     @JsonCreator
     public InvoiceJsonSimple(@JsonProperty("amount") BigDecimal amount,
-            @JsonProperty("invoiceId") String invoiceId,
-            @JsonProperty("invoiceDate") DateTime invoiceDate,
-            @JsonProperty("targetDate") DateTime targetDate,            
-            @JsonProperty("invoiceNumber") String invoiceNumber,
-            @JsonProperty("balance") BigDecimal balance) {
+                             @JsonProperty("credit") BigDecimal credit,
+                             @JsonProperty("invoiceId") String invoiceId,
+                             @JsonProperty("invoiceDate") DateTime invoiceDate,
+                             @JsonProperty("targetDate") DateTime targetDate,
+                             @JsonProperty("invoiceNumber") String invoiceNumber,
+                             @JsonProperty("balance") BigDecimal balance,
+                             @JsonProperty("accountId") String accountId) {
         super();
         this.amount = amount;
+        this.credit = credit;
         this.invoiceId = invoiceId;
         this.invoiceDate = invoiceDate;
         this.targetDate = targetDate;
         this.invoiceNumber = invoiceNumber;
         this.balance = balance;
+        this.accountId = accountId;
     }
 
     public InvoiceJsonSimple(Invoice input) {
-        this.amount = input.getTotalAmount();
+        this.amount = input.getAmountCharged();
+        this.credit = input.getAmountCredited();
         this.invoiceId = input.getId().toString();
         this.invoiceDate = input.getInvoiceDate();
         this.targetDate = input.getTargetDate();
         this.invoiceNumber = String.valueOf(input.getInvoiceNumber());
         this.balance = input.getBalance();
+        this.accountId = input.getAccountId().toString();
     }
-    
+
     public BigDecimal getAmount() {
         return amount;
     }
 
+    public BigDecimal getCredit() {
+        return credit;
+    }
+
     public String getInvoiceId() {
         return invoiceId;
     }
@@ -98,12 +105,17 @@ public class InvoiceJsonSimple {
         return balance;
     }
 
+    public String getAccountId() {
+        return accountId;
+    }
+
     @Override
     public int hashCode() {
         final int prime = 31;
         int result = 1;
         result = prime * result + ((amount == null) ? 0 : amount.hashCode());
         result = prime * result + ((balance == null) ? 0 : balance.hashCode());
+        result = prime * result + ((credit == null) ? 0 : credit.hashCode());
         result = prime * result
                 + ((invoiceDate == null) ? 0 : invoiceDate.hashCode());
         result = prime * result
@@ -127,26 +139,43 @@ public class InvoiceJsonSimple {
                 return false;
         } else if (!amount.equals(other.amount))
             return false;
+
         if (balance == null) {
             if (other.balance != null)
                 return false;
         } else if (!balance.equals(other.balance))
             return false;
+
+        if (credit == null) {
+            if (other.credit != null)
+                return false;
+        } else if (!credit.equals(other.credit))
+            return false;
+
         if (invoiceDate == null) {
             if (other.invoiceDate != null)
                 return false;
         } else if (!invoiceDate.equals(other.invoiceDate))
             return false;
+
         if (invoiceId == null) {
             if (other.invoiceId != null)
                 return false;
         } else if (!invoiceId.equals(other.invoiceId))
             return false;
+
         if (invoiceNumber == null) {
             if (other.invoiceNumber != null)
                 return false;
         } else if (!invoiceNumber.equals(other.invoiceNumber))
             return false;
+
+        if (accountId == null) {
+            if (other.accountId != null)
+                return false;
+        } else if (!accountId.equals(other.accountId))
+            return false;
+
         return true;
     }
 }
diff --git a/jaxrs/src/main/java/com/ning/billing/jaxrs/json/InvoiceJsonWithBundleKeys.java b/jaxrs/src/main/java/com/ning/billing/jaxrs/json/InvoiceJsonWithBundleKeys.java
index 98e2844..da3ccf4 100644
--- a/jaxrs/src/main/java/com/ning/billing/jaxrs/json/InvoiceJsonWithBundleKeys.java
+++ b/jaxrs/src/main/java/com/ning/billing/jaxrs/json/InvoiceJsonWithBundleKeys.java
@@ -1,13 +1,13 @@
 package com.ning.billing.jaxrs.json;
 import java.math.BigDecimal;
 
-import com.fasterxml.jackson.annotation.JsonCreator;
-import com.fasterxml.jackson.annotation.JsonProperty;
 import org.joda.time.DateTime;
 
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonProperty;
 import com.ning.billing.invoice.api.Invoice;
 
-/* 
+/*
  * Copyright 2010-2011 Ning, Inc.
  *
  * Ning licenses this file to you under the Apache License, version 2.0
@@ -24,25 +24,24 @@ import com.ning.billing.invoice.api.Invoice;
  */
 
 public class InvoiceJsonWithBundleKeys extends InvoiceJsonSimple {
-    
-    
     private final String bundleKeys;
 
-
     public InvoiceJsonWithBundleKeys() {
         super();
         this.bundleKeys = null;
     }
-    
+
     @JsonCreator
     public InvoiceJsonWithBundleKeys(@JsonProperty("amount") BigDecimal amount,
-            @JsonProperty("invoiceId") String invoiceId,
-            @JsonProperty("invoiceDate") DateTime invoiceDate,
-            @JsonProperty("targetDate") DateTime targetDate,            
-            @JsonProperty("invoiceNumber") String invoiceNumber,
-            @JsonProperty("balance") BigDecimal balance,
-            @JsonProperty("externalBundleKeys") String bundleKeys) {
-        super(amount, invoiceId, invoiceDate, targetDate, invoiceNumber, balance);
+                                     @JsonProperty("credit") BigDecimal credit,
+                                     @JsonProperty("invoiceId") String invoiceId,
+                                     @JsonProperty("invoiceDate") DateTime invoiceDate,
+                                     @JsonProperty("targetDate") DateTime targetDate,
+                                     @JsonProperty("invoiceNumber") String invoiceNumber,
+                                     @JsonProperty("balance") BigDecimal balance,
+                                     @JsonProperty("accountId") String accountId,
+                                     @JsonProperty("externalBundleKeys") String bundleKeys) {
+        super(amount, credit, invoiceId, invoiceDate, targetDate, invoiceNumber, balance, accountId);
         this.bundleKeys = bundleKeys;
     }
 
diff --git a/jaxrs/src/main/java/com/ning/billing/jaxrs/json/InvoiceJsonWithItems.java b/jaxrs/src/main/java/com/ning/billing/jaxrs/json/InvoiceJsonWithItems.java
new file mode 100644
index 0000000..b68d34d
--- /dev/null
+++ b/jaxrs/src/main/java/com/ning/billing/jaxrs/json/InvoiceJsonWithItems.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2010-2011 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.
+ */
+
+package com.ning.billing.jaxrs.json;
+
+import java.math.BigDecimal;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import org.joda.time.DateTime;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.ning.billing.invoice.api.Invoice;
+import com.ning.billing.invoice.api.InvoiceItem;
+
+public class InvoiceJsonWithItems extends InvoiceJsonSimple {
+    private final List<InvoiceItemJsonSimple> items;
+
+    @JsonCreator
+    public InvoiceJsonWithItems(@JsonProperty("amount") BigDecimal amount,
+                                @JsonProperty("credit") BigDecimal credit,
+                                @JsonProperty("invoiceId") String invoiceId,
+                                @JsonProperty("invoiceDate") DateTime invoiceDate,
+                                @JsonProperty("targetDate") DateTime targetDate,
+                                @JsonProperty("invoiceNumber") String invoiceNumber,
+                                @JsonProperty("balance") BigDecimal balance,
+                                @JsonProperty("accountId") String accountId,
+            @JsonProperty("items") List<InvoiceItemJsonSimple> items) {
+        super(amount, credit, invoiceId, invoiceDate, targetDate, invoiceNumber, balance, accountId);
+        this.items = new ArrayList<InvoiceItemJsonSimple>(items);
+    }
+
+    public InvoiceJsonWithItems(Invoice input) {
+        super(input);
+        this.items = new ArrayList<InvoiceItemJsonSimple>(input.getInvoiceItems().size());
+        for (InvoiceItem item : input.getInvoiceItems()) {
+            this.items.add(new InvoiceItemJsonSimple(item));
+        }
+    }
+
+    public List<InvoiceItemJsonSimple> getItems() {
+        return Collections.unmodifiableList(items);
+    }
+}
diff --git a/jaxrs/src/main/java/com/ning/billing/jaxrs/json/PaymentJsonSimple.java b/jaxrs/src/main/java/com/ning/billing/jaxrs/json/PaymentJsonSimple.java
index c9ccd59..1eafded 100644
--- a/jaxrs/src/main/java/com/ning/billing/jaxrs/json/PaymentJsonSimple.java
+++ b/jaxrs/src/main/java/com/ning/billing/jaxrs/json/PaymentJsonSimple.java
@@ -19,11 +19,10 @@ package com.ning.billing.jaxrs.json;
 import java.math.BigDecimal;
 import java.util.UUID;
 
-import com.fasterxml.jackson.annotation.JsonCreator;
-import com.fasterxml.jackson.annotation.JsonProperty;
-import com.fasterxml.jackson.annotation.JsonView;
 import org.joda.time.DateTime;
 
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonProperty;
 import com.ning.billing.payment.api.Payment;
 import com.ning.billing.payment.api.PaymentInfoEvent;
 import com.ning.billing.util.clock.DefaultClock;
@@ -41,15 +40,15 @@ public class PaymentJsonSimple {
     private final String paymentId;
     
     private final DateTime requestedDate;
-    
+
     private final DateTime effectiveDate;
-    
+
     private final Integer retryCount;
-    
+
     private final String currency;
-    
+
     private final String status;
-      
+
     public PaymentJsonSimple() {
         this.amount = null;
         this.paidAmount = null;
@@ -72,7 +71,7 @@ public class PaymentJsonSimple {
             @JsonProperty("requestedDate") DateTime requestedDate,
             @JsonProperty("effectiveDate") DateTime effectiveDate,
             @JsonProperty("retryCount") Integer retryCount,
-            @JsonProperty("currency") String currency,            
+            @JsonProperty("currency") String currency,
             @JsonProperty("status") String status) {
         super();
         this.amount = amount;
diff --git a/jaxrs/src/main/java/com/ning/billing/jaxrs/json/PaymentJsonWithBundleKeys.java b/jaxrs/src/main/java/com/ning/billing/jaxrs/json/PaymentJsonWithBundleKeys.java
index 839f70d..8f9cf08 100644
--- a/jaxrs/src/main/java/com/ning/billing/jaxrs/json/PaymentJsonWithBundleKeys.java
+++ b/jaxrs/src/main/java/com/ning/billing/jaxrs/json/PaymentJsonWithBundleKeys.java
@@ -19,14 +19,15 @@ package com.ning.billing.jaxrs.json;
 import java.math.BigDecimal;
 import java.util.UUID;
 
+import org.joda.time.DateTime;
+
 import com.fasterxml.jackson.annotation.JsonCreator;
 import com.fasterxml.jackson.annotation.JsonProperty;
-import org.joda.time.DateTime;
 
 public class PaymentJsonWithBundleKeys extends PaymentJsonSimple {
 
     private final String bundleKeys;
-    
+
     public PaymentJsonWithBundleKeys() {
         super();
         this.bundleKeys = null;
@@ -41,13 +42,14 @@ public class PaymentJsonWithBundleKeys extends PaymentJsonSimple {
             @JsonProperty("requestedDt") DateTime requestedDate,
             @JsonProperty("effectiveDt") DateTime effectiveDate,
             @JsonProperty("retryCount") Integer retryCount,
-            @JsonProperty("currency") String currency,            
+            @JsonProperty("currency") String currency,
             @JsonProperty("status") String status,
             @JsonProperty("externalBundleKeys") String bundleKeys) {
         super(amount, paidAmount, accountId, invoiceId, paymentId, requestedDate, effectiveDate, retryCount, currency, status);
+
         this.bundleKeys = bundleKeys;
     }
-    
+
     public String getBundleKeys() {
         return bundleKeys;
     }
diff --git a/jaxrs/src/main/java/com/ning/billing/jaxrs/json/PlanDetailJason.java b/jaxrs/src/main/java/com/ning/billing/jaxrs/json/PlanDetailJason.java
new file mode 100644
index 0000000..7474315
--- /dev/null
+++ b/jaxrs/src/main/java/com/ning/billing/jaxrs/json/PlanDetailJason.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2010-2011 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.
+ */
+
+package com.ning.billing.jaxrs.json;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.ning.billing.catalog.api.BillingPeriod;
+import com.ning.billing.catalog.api.InternationalPrice;
+import com.ning.billing.catalog.api.Listing;
+
+public class PlanDetailJason {
+
+    final String productName;
+    final  String planName;
+    final  BillingPeriod billingPeriod;
+    final  String priceListName;
+    final  InternationalPrice finalPhasePrice;
+    public PlanDetailJason(
+            @JsonProperty("product") String productName,
+            @JsonProperty("plan") String planName,
+            @JsonProperty("final_phase_billing_period") BillingPeriod billingPeriod,
+            @JsonProperty("priceList") String priceListName,
+            @JsonProperty("final_phase_recurring_price") InternationalPrice finalPhasePrice
+                ) {
+        this.productName = productName;
+        this.planName = planName;
+        this.billingPeriod = billingPeriod;
+        this.priceListName = priceListName;
+        this.finalPhasePrice = finalPhasePrice;
+    }
+    
+    public PlanDetailJason(Listing listing) {
+        this.productName = listing.getPlan().getProduct().getName();
+        this.planName = listing.getPlan().getName();
+        this.billingPeriod = listing.getPlan().getBillingPeriod();
+        this.priceListName = listing.getPriceList().getName();
+        this.finalPhasePrice = listing.getPlan().getFinalPhase().getRecurringPrice();
+    }
+
+    public String getProductName() {
+        return productName;
+    }
+
+    public String getPlanName() {
+        return planName;
+    }
+
+    public BillingPeriod getBillingPeriod() {
+        return billingPeriod;
+    }
+
+    public String getPriceListName() {
+        return priceListName;
+    }
+
+    public InternationalPrice getFinalPhasePrice() {
+        return finalPhasePrice;
+    }
+    
+ 
+}
diff --git a/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/CatalogResource.java b/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/CatalogResource.java
new file mode 100644
index 0000000..ec6e5c0
--- /dev/null
+++ b/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/CatalogResource.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright 2010-2011 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.
+ */
+
+package com.ning.billing.jaxrs.resources;
+
+import static javax.ws.rs.core.MediaType.APPLICATION_JSON;
+import static javax.ws.rs.core.MediaType.APPLICATION_XML;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.Response.Status;
+
+import com.google.inject.Inject;
+import com.google.inject.Singleton;
+import com.ning.billing.catalog.api.CatalogApiException;
+import com.ning.billing.catalog.api.CatalogService;
+import com.ning.billing.catalog.api.Listing;
+import com.ning.billing.catalog.api.StaticCatalog;
+import com.ning.billing.jaxrs.json.PlanDetailJason;
+import com.ning.billing.util.config.XMLWriter;
+
+@Singleton
+@Path(JaxrsResource.CATALOG_PATH)
+public class CatalogResource implements JaxrsResource {
+
+    private CatalogService catalogService;
+
+    @Inject
+    public CatalogResource(final CatalogService catalogService)
+    {
+        this.catalogService = catalogService;
+    }
+
+    @GET
+    @Produces(APPLICATION_XML)
+    public Response getCatalog() throws Exception {
+        return Response.status(Status.OK).entity(XMLWriter.writeXML((StaticCatalog) catalogService.getCurrentCatalog(), StaticCatalog.class)).build();
+    }
+
+    // Need to figure out dependency on StandaloneCatalog
+    //    @GET
+    //    @Path("/xsd")
+    //    @Produces(APPLICATION_XML)
+    //    public String getCatalogXsd() throws Exception
+    //    {
+    //        InputStream stream = XMLSchemaGenerator.xmlSchema(StandaloneCatalog.class);
+    //        StringWriter writer = new StringWriter();
+    //        IOUtils.copy(stream, writer);
+    //        String result = writer.toString();
+    //
+    //        return result;
+    //    }
+
+
+
+    @GET
+    @Path("/availableAddons")
+    @Produces(APPLICATION_JSON)
+    public Response getAvailableAddons(@QueryParam("baseProductName") final String baseProductName) throws CatalogApiException {
+        StaticCatalog catalog = catalogService.getCurrentCatalog();
+        List<Listing> listings = catalog.getAvailableAddonListings(baseProductName);
+        List<PlanDetailJason> details = new ArrayList<PlanDetailJason>();
+        for(Listing listing : listings) {
+            details.add(new PlanDetailJason(listing));
+        }
+        return Response.status(Status.OK).entity(details).build();
+    }
+}
diff --git a/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/ChargebackResource.java b/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/ChargebackResource.java
new file mode 100644
index 0000000..b1b63ec
--- /dev/null
+++ b/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/ChargebackResource.java
@@ -0,0 +1,167 @@
+/*
+ * Copyright 2010-2011 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.
+ */
+
+package com.ning.billing.jaxrs.resources;
+
+import static javax.ws.rs.core.MediaType.APPLICATION_JSON;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.UUID;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.GET;
+import javax.ws.rs.HeaderParam;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.Response;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.base.Predicate;
+import com.google.common.collect.Collections2;
+import com.google.inject.Inject;
+import com.google.inject.Singleton;
+import com.ning.billing.invoice.api.InvoiceApiException;
+import com.ning.billing.invoice.api.InvoicePayment;
+import com.ning.billing.invoice.api.InvoicePaymentApi;
+import com.ning.billing.jaxrs.json.ChargebackCollectionJson;
+import com.ning.billing.jaxrs.json.ChargebackJson;
+import com.ning.billing.jaxrs.util.Context;
+import com.ning.billing.jaxrs.util.JaxrsUriBuilder;
+import com.ning.billing.payment.api.Payment;
+import com.ning.billing.payment.api.Payment.PaymentAttempt;
+import com.ning.billing.payment.api.PaymentApi;
+import com.ning.billing.payment.api.PaymentApiException;
+import com.ning.billing.payment.api.PaymentStatus;
+
+@Singleton
+@Path(JaxrsResource.CHARGEBACKS_PATH)
+public class ChargebackResource implements JaxrsResource {
+    private static final Logger log = LoggerFactory.getLogger(ChargebackResource.class);
+
+    private final JaxrsUriBuilder uriBuilder;
+    private final InvoicePaymentApi invoicePaymentApi;
+    private final PaymentApi paymentApi;
+    private final Context context;
+
+    @Inject
+    public ChargebackResource(final JaxrsUriBuilder uriBuilder,
+                              final InvoicePaymentApi invoicePaymentApi,
+                              final PaymentApi paymentApi,
+                              final Context context) {
+        this.uriBuilder = uriBuilder;
+        this.invoicePaymentApi = invoicePaymentApi;
+        this.paymentApi = paymentApi;
+        this.context = context;
+    }
+
+    @GET
+    @Path("/{chargebackId:" + UUID_PATTERN + "}")
+    @Produces(APPLICATION_JSON)
+    public Response getChargeback(@PathParam("chargebackId") String chargebackId) {
+        try {
+            InvoicePayment chargeback = invoicePaymentApi.getChargebackById(UUID.fromString(chargebackId));
+            ChargebackJson chargebackJson = new ChargebackJson(chargeback);
+
+            return Response.status(Response.Status.OK).entity(chargebackJson).build();
+        } catch (InvoiceApiException e) {
+            final String error = String.format("Failed to locate chargeback for id %s", chargebackId);
+            log.info(error, e);
+            return Response.status(Response.Status.NO_CONTENT).build();
+        }
+    }
+
+    @GET
+    @Path("/accounts/{accountId:" + UUID_PATTERN + "}")
+    @Produces(APPLICATION_JSON)
+    public Response getForAccount(@PathParam("accountId") String accountId) {
+        List<InvoicePayment> chargebacks = invoicePaymentApi.getChargebacksByAccountId(UUID.fromString(accountId));
+        List<ChargebackJson> chargebacksJson = convertToJson(chargebacks);
+
+        ChargebackCollectionJson json = new ChargebackCollectionJson(accountId, chargebacksJson);
+        return Response.status(Response.Status.OK).entity(json).build();
+    }
+
+    @GET
+    @Path("/payments/{paymentId:" + UUID_PATTERN + "}")
+    @Produces(APPLICATION_JSON)
+    public Response getForPayment(@PathParam("paymentId") String paymentId) {
+        
+        try {
+            Payment payment = paymentApi.getPayment(UUID.fromString(paymentId));
+            Collection<PaymentAttempt> attempts = Collections2.filter(payment.getAttempts(), new Predicate<PaymentAttempt>() {
+                @Override
+                public boolean apply(PaymentAttempt input) {
+                    return input.getPaymentStatus() == PaymentStatus.SUCCESS;
+                }
+            });
+            if (attempts.size() == 0) {
+                final String error = String.format("Failed to locate succesful payment attempts for paymentId %s", paymentId);
+                return Response.status(Response.Status.NO_CONTENT).entity(error).build();
+            }
+            UUID paymentAttemptId = attempts.iterator().next().getId();
+            
+            List<InvoicePayment> chargebacks = invoicePaymentApi.getChargebacksByPaymentAttemptId(paymentAttemptId);
+            List<ChargebackJson> chargebacksJson = convertToJson(chargebacks);
+
+            String accountId = invoicePaymentApi.getAccountIdFromInvoicePaymentId(UUID.fromString(paymentId)).toString();
+
+            ChargebackCollectionJson json = new ChargebackCollectionJson(accountId, chargebacksJson);
+            return Response.status(Response.Status.OK).entity(json).build();
+        
+        } catch (PaymentApiException e) {
+            final String error = String.format("Failed to locate payment attempt for payment id %s", paymentId);
+            return Response.status(Response.Status.NO_CONTENT).entity(error).build();
+        } catch (InvoiceApiException e) {
+            final String error = String.format("Failed to locate account for payment id %s", paymentId);
+            return Response.status(Response.Status.NO_CONTENT).entity(error).build();
+        }
+    }
+
+    @POST
+    @Consumes(APPLICATION_JSON)
+    @Produces(APPLICATION_JSON)
+    public Response createChargeback(final ChargebackJson json,
+                                     @HeaderParam(HDR_CREATED_BY) final String createdBy,
+                                     @HeaderParam(HDR_REASON) final String reason,
+                                     @HeaderParam(HDR_COMMENT) final String comment) {
+        try {
+            invoicePaymentApi.processChargeback(UUID.fromString(json.getPaymentId()), json.getChargebackAmount(),
+                                                context.createContext(createdBy, reason, comment));
+            return uriBuilder.buildResponse(ChargebackResource.class, "getChargeback", json.getPaymentId());
+        } catch (InvoiceApiException e) {
+            final String error = String.format("Failed to create chargeback %s", json);
+            log.info(error, e);
+            return Response.status(Response.Status.BAD_REQUEST).entity(error).build();
+        } catch (IllegalArgumentException e) {
+            return Response.status(Response.Status.BAD_REQUEST).entity(e.getMessage()).build();
+        }
+    }
+
+    private List<ChargebackJson> convertToJson(List<InvoicePayment> chargebacks) {
+        List<ChargebackJson> result = new ArrayList<ChargebackJson>();
+        for (InvoicePayment chargeback : chargebacks) {
+            result.add(new ChargebackJson(chargeback));
+        }
+
+        return result;
+    }
+}
\ No newline at end of file
diff --git a/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/CreditResource.java b/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/CreditResource.java
index 076dbff..226457e 100644
--- a/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/CreditResource.java
+++ b/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/CreditResource.java
@@ -16,6 +16,123 @@
 
 package com.ning.billing.jaxrs.resources;
 
-public class CreditResource {
+import static javax.ws.rs.core.MediaType.APPLICATION_JSON;
+
+import java.util.UUID;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.GET;
+import javax.ws.rs.HeaderParam;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.Response;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.inject.Inject;
+import com.google.inject.Singleton;
+import com.ning.billing.account.api.Account;
+import com.ning.billing.account.api.AccountApiException;
+import com.ning.billing.account.api.AccountUserApi;
+import com.ning.billing.invoice.api.InvoiceApiException;
+import com.ning.billing.invoice.api.InvoiceItem;
+import com.ning.billing.invoice.api.InvoiceUserApi;
+import com.ning.billing.jaxrs.json.CreditJson;
+import com.ning.billing.jaxrs.util.Context;
+import com.ning.billing.jaxrs.util.JaxrsUriBuilder;
+
+@Singleton
+@Path(JaxrsResource.CREDITS_PATH)
+public class CreditResource implements JaxrsResource {
+    private static final Logger log = LoggerFactory.getLogger(CreditResource.class);
+
+    private final JaxrsUriBuilder uriBuilder;
+    private final InvoiceUserApi invoiceUserApi;
+    private final AccountUserApi accountUserApi;
+    private final Context context;
+
+    @Inject
+    public CreditResource(JaxrsUriBuilder uriBuilder, InvoiceUserApi invoiceUserApi, AccountUserApi accountUserApi, Context context) {
+        this.uriBuilder = uriBuilder;
+        this.invoiceUserApi = invoiceUserApi;
+        this.accountUserApi = accountUserApi;
+        this.context = context;
+    }
+
+    @GET
+    @Path("/{creditId:" + UUID_PATTERN + "}")
+    @Produces(APPLICATION_JSON)
+    public Response getCredit(@PathParam("creditId") String creditId) {
+        try {
+            InvoiceItem credit = invoiceUserApi.getCreditById(UUID.fromString(creditId));
+            CreditJson creditJson = new CreditJson(credit);
+
+            return Response.status(Response.Status.OK).entity(creditJson).build();
+        } catch (InvoiceApiException e) {
+            final String error = String.format("Failed to locate credit for id %s", creditId);
+            log.info(error, e);
+            return Response.status(Response.Status.NO_CONTENT).build();
+        }
+    }
+
+    @POST
+    @Path("/accounts/{accountId:" + UUID_PATTERN + "}")
+    @Consumes(APPLICATION_JSON)
+    @Produces(APPLICATION_JSON)
+    public Response createCredit(final CreditJson json,
+                                 @PathParam("accountId") final String accountId,
+                                 @HeaderParam(HDR_CREATED_BY) final String createdBy,
+                                 @HeaderParam(HDR_REASON) final String reason,
+                                 @HeaderParam(HDR_COMMENT) final String comment) {
+        try {
+            Account account = accountUserApi.getAccountById(UUID.fromString(accountId));
+
+            InvoiceItem credit = invoiceUserApi.insertCredit(account.getId(), json.getCreditAmount(), json.getEffectiveDate(),
+                                                             account.getCurrency(), context.createContext(createdBy, reason, comment));
+
+            return uriBuilder.buildResponse(ChargebackResource.class, "getCredit", credit.getId());
+        } catch (InvoiceApiException e) {
+            final String error = String.format("Failed to create credit %s", json);
+            log.info(error, e);
+            return Response.status(Response.Status.BAD_REQUEST).entity(error).build();
+        } catch (IllegalArgumentException e) {
+            return Response.status(Response.Status.BAD_REQUEST).entity(e.getMessage()).build();
+        } catch (AccountApiException e) {
+            final String error = String.format("Failed to create credit %s", json);
+            log.info(error, e);
+            return Response.status(Response.Status.BAD_REQUEST).entity(error).build();
+        }
+    }
+
 
 }
+
+
+//POST /1.0/accounts/<account_id>/credits	 	 Creates a credit for that account	 201 (CREATED), 400 (BAD_REQUEST), 404 (NOT_FOUND)
+//Semantics:
+//
+//All operations are synchronous
+//JSON Credit GET:
+// {
+//   "accountId" : "theAccountId",
+//   "credits" : [{
+//      "requestedDate" : "2012-05-12T15:34:22.000Z",
+//      "effectiveDate" : "2012-05-12T15:34:22.000Z",
+//      "creditAmount" : 54.32,
+//      "invoiceId" : "theInvoiceId",
+//      "invoiceNumber" : "TheInvoiceNumber",
+//      "reason" : "whatever"
+//   }]
+// }
+//
+//JSON Credit POST:
+// {
+//   "creditAmount" : 54.32,
+//   "reason" : "whatever",
+//   "requestedDate" : "2012-05-12T15:34:22.000Z",
+//   "invoiceId" : "theInvoiceId",
+//   "invoiceNumber" : "TheInvoiceNumber"
+// }
\ No newline at end of file
diff --git a/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/InvoiceResource.java b/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/InvoiceResource.java
index 46c7a48..b1ab41f 100644
--- a/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/InvoiceResource.java
+++ b/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/InvoiceResource.java
@@ -33,15 +33,9 @@ import javax.ws.rs.Path;
 import javax.ws.rs.PathParam;
 import javax.ws.rs.Produces;
 import javax.ws.rs.QueryParam;
-
 import javax.ws.rs.core.Response;
 import javax.ws.rs.core.Response.Status;
 
-import com.ning.billing.jaxrs.json.CustomFieldJson;
-import com.ning.billing.jaxrs.util.TagHelper;
-import com.ning.billing.util.api.CustomFieldUserApi;
-import com.ning.billing.util.api.TagUserApi;
-import com.ning.billing.util.dao.ObjectType;
 import org.joda.time.DateTime;
 import org.joda.time.format.DateTimeFormatter;
 import org.joda.time.format.ISODateTimeFormat;
@@ -56,14 +50,19 @@ import com.ning.billing.account.api.AccountUserApi;
 import com.ning.billing.invoice.api.Invoice;
 import com.ning.billing.invoice.api.InvoiceApiException;
 import com.ning.billing.invoice.api.InvoiceUserApi;
-
+import com.ning.billing.jaxrs.json.CustomFieldJson;
 import com.ning.billing.jaxrs.json.InvoiceJsonSimple;
+import com.ning.billing.jaxrs.json.InvoiceJsonWithItems;
 import com.ning.billing.jaxrs.json.PaymentJsonSimple;
 import com.ning.billing.jaxrs.util.Context;
 import com.ning.billing.jaxrs.util.JaxrsUriBuilder;
+import com.ning.billing.jaxrs.util.TagHelper;
 import com.ning.billing.payment.api.Payment;
 import com.ning.billing.payment.api.PaymentApi;
 import com.ning.billing.payment.api.PaymentApiException;
+import com.ning.billing.util.api.CustomFieldUserApi;
+import com.ning.billing.util.api.TagUserApi;
+import com.ning.billing.util.dao.ObjectType;
 
 
 @Path(JaxrsResource.INVOICES_PATH)
@@ -76,13 +75,13 @@ public class InvoiceResource extends JaxRsResourceBase {
     private static final String TAG_URI = JaxrsResource.TAGS + "/{" + ID_PARAM_NAME + ":" + UUID_PATTERN + "}";
 
     private final DateTimeFormatter DATE_TIME_FORMATTER = ISODateTimeFormat.dateTime();
-    
+
     private final AccountUserApi accountApi;
     private final InvoiceUserApi invoiceApi;
     private final PaymentApi paymentApi;
     private final Context context;
     private final JaxrsUriBuilder uriBuilder;
-    
+
     @Inject
     public InvoiceResource(final AccountUserApi accountApi,
             final InvoiceUserApi invoiceApi,
@@ -99,12 +98,12 @@ public class InvoiceResource extends JaxRsResourceBase {
         this.context = context;
         this.uriBuilder = uriBuilder;
     }
-    
+
     @GET
     @Produces(APPLICATION_JSON)
     public Response getInvoices(@QueryParam(QUERY_ACCOUNT_ID) final String accountId) {
         try {
-            
+
             Preconditions.checkNotNull(accountId, "% query parameter must be specified", QUERY_ACCOUNT_ID);
             accountApi.getAccountById(UUID.fromString(accountId));
             List<Invoice> invoices = invoiceApi.getInvoicesByAccount(UUID.fromString(accountId));
@@ -114,18 +113,18 @@ public class InvoiceResource extends JaxRsResourceBase {
             }
             return Response.status(Status.OK).entity(result).build();
         } catch (AccountApiException e) {
-            return Response.status(Status.NO_CONTENT).build();            
+            return Response.status(Status.NO_CONTENT).build();
         } catch (NullPointerException e) {
-            return Response.status(Status.BAD_REQUEST).build();            
+            return Response.status(Status.BAD_REQUEST).build();
         }
     }
 
     @GET
     @Path("/{invoiceId:\\w+-\\w+-\\w+-\\w+-\\w+}")
     @Produces(APPLICATION_JSON)
-    public Response getInvoice(@PathParam("invoiceId") String invoiceId) {
+    public Response getInvoice(@PathParam("invoiceId") String invoiceId, @QueryParam("withItems") @DefaultValue("false") boolean withItems) {
         Invoice invoice = invoiceApi.getInvoice(UUID.fromString(invoiceId));
-        InvoiceJsonSimple json = new InvoiceJsonSimple(invoice);
+        InvoiceJsonSimple json = withItems ? new InvoiceJsonWithItems(invoice) : new InvoiceJsonSimple(invoice);  
         return Response.status(Status.OK).entity(json).build();
     }
 
@@ -141,12 +140,12 @@ public class InvoiceResource extends JaxRsResourceBase {
             @HeaderParam(HDR_COMMENT) final String comment) {
 
         try {
-            
+
             Preconditions.checkNotNull(accountId, "% needs to be specified", QUERY_ACCOUNT_ID);
             Preconditions.checkNotNull(targetDate, "% needs to be specified", QUERY_TARGET_DATE);
-            
-            DateTime inputDate = (targetDate != null) ? DATE_TIME_FORMATTER.parseDateTime(targetDate) : null;        
-            
+
+            DateTime inputDate = (targetDate != null) ? DATE_TIME_FORMATTER.parseDateTime(targetDate) : null;
+
             accountApi.getAccountById(UUID.fromString(accountId));
             Invoice generatedInvoice = invoiceApi.triggerInvoiceGeneration(UUID.fromString(accountId), inputDate, dryRun.booleanValue(),
                     context.createContext(createdBy, reason, comment));
@@ -156,11 +155,11 @@ public class InvoiceResource extends JaxRsResourceBase {
                return uriBuilder.buildResponse(InvoiceResource.class, "getInvoice", generatedInvoice.getId());
             }
         } catch (AccountApiException e) {
-            return Response.status(Status.BAD_REQUEST).entity(e.getMessage()).build();  
+            return Response.status(Status.BAD_REQUEST).entity(e.getMessage()).build();
         } catch (InvoiceApiException e) {
-            return Response.status(Status.BAD_REQUEST).entity(e.getMessage()).build();  
+            return Response.status(Status.BAD_REQUEST).entity(e.getMessage()).build();
         } catch (NullPointerException e) {
-            return Response.status(Status.BAD_REQUEST).entity(e.getMessage()).build();            
+            return Response.status(Status.BAD_REQUEST).entity(e.getMessage()).build();
         }
     }
 
diff --git a/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/JaxrsResource.java b/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/JaxrsResource.java
index f334d77..b9be27c 100644
--- a/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/JaxrsResource.java
+++ b/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/JaxrsResource.java
@@ -75,7 +75,17 @@ public interface JaxrsResource {
 
     public static final String PAYMENTS = "payments";     
     public static final String PAYMENTS_PATH = PREFIX + "/" + PAYMENTS;    
-    
+
+    public static final String CREDITS = "credits";
+    public static final String CREDITS_PATH = PREFIX + "/" + CREDITS;
+
+    public static final String CHARGEBACKS = "chargebacks";
+    public static final String CHARGEBACKS_PATH = PREFIX + "/" + CHARGEBACKS;
+
     public static final String TAGS = "tags";
     public static final String CUSTOM_FIELDS = "custom_fields";
+    
+    public static final String CATALOG = "catalog";
+    public static final String CATALOG_PATH = PREFIX + "/" + CATALOG;
+
 }
diff --git a/jaxrs/src/main/java/com/ning/billing/jaxrs/util/Context.java b/jaxrs/src/main/java/com/ning/billing/jaxrs/util/Context.java
index f6b7d67..c49616b 100644
--- a/jaxrs/src/main/java/com/ning/billing/jaxrs/util/Context.java
+++ b/jaxrs/src/main/java/com/ning/billing/jaxrs/util/Context.java
@@ -43,7 +43,7 @@ public class Context {
     throws IllegalArgumentException {
         try {
             Preconditions.checkNotNull(createdBy, String.format("Header %s needs to be set", JaxrsResource.HDR_CREATED_BY));
-            return contextFactory.createCallContext(createdBy, origin, userType, UUID.randomUUID());
+            return contextFactory.createCallContext(createdBy, origin, userType, reason, comment, UUID.randomUUID());
         } catch (NullPointerException e) {
             throw new IllegalArgumentException(e.getMessage());
         }

junction/pom.xml 19(+12 -7)

diff --git a/junction/pom.xml b/junction/pom.xml
index 4efa966..1824a28 100644
--- a/junction/pom.xml
+++ b/junction/pom.xml
@@ -1,12 +1,12 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <!-- ~ Copyright 2010-2011 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. -->
+	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. -->
 
 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
     <modelVersion>4.0.0</modelVersion>
@@ -73,6 +73,11 @@
             <artifactId>killbill-catalog</artifactId>
             <scope>test</scope>
         </dependency>
+        <dependency>
+            <groupId>org.mockito</groupId>
+            <artifactId>mockito-all</artifactId>
+            <scope>test</scope>
+        </dependency>
          <dependency>
             <groupId>mysql</groupId>
             <artifactId>mysql-connector-java</artifactId>
diff --git a/junction/src/main/java/com/ning/billing/junction/plumbing/billing/BlockingCalculator.java b/junction/src/main/java/com/ning/billing/junction/plumbing/billing/BlockingCalculator.java
index 999c5d6..e33c063 100644
--- a/junction/src/main/java/com/ning/billing/junction/plumbing/billing/BlockingCalculator.java
+++ b/junction/src/main/java/com/ning/billing/junction/plumbing/billing/BlockingCalculator.java
@@ -23,6 +23,7 @@ import java.util.List;
 import java.util.SortedSet;
 import java.util.TreeSet;
 import java.util.UUID;
+import java.util.concurrent.atomic.AtomicLong;
 
 import org.joda.time.DateTime;
 
@@ -36,12 +37,13 @@ import com.ning.billing.entitlement.api.SubscriptionTransitionType;
 import com.ning.billing.entitlement.api.billing.BillingEvent;
 import com.ning.billing.entitlement.api.billing.BillingModeType;
 import com.ning.billing.entitlement.api.user.Subscription;
-import com.ning.billing.junction.api.Blockable;
 import com.ning.billing.junction.api.BlockingApi;
 import com.ning.billing.junction.api.BlockingState;
 import com.ning.billing.junction.api.DefaultBlockingState;
 
 public class BlockingCalculator {
+    private static AtomicLong globaltotalOrder = new AtomicLong();
+    
     private final BlockingApi blockingApi;
 
     protected static class DisabledDuration {
@@ -196,7 +198,7 @@ public class BlockingCalculator {
         final BillingModeType billingModeType = previousEvent.getBillingMode();
         final BillingPeriod billingPeriod = previousEvent.getBillingPeriod();
         final SubscriptionTransitionType type = SubscriptionTransitionType.CANCEL;
-        final Long totalOrdering = 0L; //TODO
+        final Long totalOrdering = globaltotalOrder.getAndIncrement(); 
 
         return new DefaultBillingEvent(account, subscription, effectiveDate, plan, planPhase,
                 fixedPrice, recurringPrice, currency,
@@ -218,7 +220,7 @@ public class BlockingCalculator {
         final BillingModeType billingModeType = previousEvent.getBillingMode();
         final BillingPeriod billingPeriod = previousEvent.getBillingPeriod();
         final SubscriptionTransitionType type = SubscriptionTransitionType.RE_CREATE;
-        final Long totalOrdering = 0L; //TODO
+        final Long totalOrdering = globaltotalOrder.getAndIncrement();  
 
         return new DefaultBillingEvent(account, subscription, effectiveDate, plan, planPhase,
                 fixedPrice, recurringPrice, currency,
diff --git a/junction/src/main/java/com/ning/billing/junction/plumbing/billing/DefaultBillingApi.java b/junction/src/main/java/com/ning/billing/junction/plumbing/billing/DefaultBillingApi.java
index 43e8a2a..6d91e7c 100644
--- a/junction/src/main/java/com/ning/billing/junction/plumbing/billing/DefaultBillingApi.java
+++ b/junction/src/main/java/com/ning/billing/junction/plumbing/billing/DefaultBillingApi.java
@@ -16,7 +16,9 @@
 
 package com.ning.billing.junction.plumbing.billing;
 
+import java.util.Iterator;
 import java.util.List;
+import java.util.Map;
 import java.util.SortedSet;
 import java.util.UUID;
 
@@ -41,10 +43,14 @@ import com.ning.billing.entitlement.api.user.SubscriptionBundle;
 import com.ning.billing.entitlement.api.user.SubscriptionEvent;
 import com.ning.billing.junction.api.BillingApi;
 import com.ning.billing.junction.api.BillingEventSet;
+import com.ning.billing.util.api.TagUserApi;
 import com.ning.billing.util.callcontext.CallContext;
 import com.ning.billing.util.callcontext.CallContextFactory;
 import com.ning.billing.util.callcontext.CallOrigin;
 import com.ning.billing.util.callcontext.UserType;
+import com.ning.billing.util.dao.ObjectType;
+import com.ning.billing.util.tag.ControlTagType;
+import com.ning.billing.util.tag.Tag;
 
 public class DefaultBillingApi implements BillingApi {
     private static final String API_USER_NAME = "Billing Api";
@@ -56,12 +62,12 @@ public class DefaultBillingApi implements BillingApi {
     private final EntitlementUserApi entitlementUserApi;
     private final CatalogService catalogService;
     private final BlockingCalculator blockCalculator;
-//    private final TagUserApi tagApi;
+    private final TagUserApi tagApi;
 
     @Inject
     public DefaultBillingApi(final ChargeThruApi chargeThruApi, final CallContextFactory factory, final AccountUserApi accountApi, 
             final BillCycleDayCalculator bcdCalculator, final EntitlementUserApi entitlementUserApi, final BlockingCalculator blockCalculator,
-            final CatalogService catalogService) { //, final TagUserApi tagApi) {
+            final CatalogService catalogService, final TagUserApi tagApi) {
 
         this.chargeThruApi = chargeThruApi;
         this.accountApi = accountApi;
@@ -70,53 +76,91 @@ public class DefaultBillingApi implements BillingApi {
         this.entitlementUserApi = entitlementUserApi;
         this.catalogService = catalogService;
         this.blockCalculator = blockCalculator;
-  //      this.tagApi = tagApi;
+        this.tagApi = tagApi;
     }
 
     @Override
-    public SortedSet<BillingEvent> getBillingEventsForAccountAndUpdateAccountBCD(final UUID accountId) {
+    public BillingEventSet getBillingEventsForAccountAndUpdateAccountBCD(final UUID accountId) {
         CallContext context = factory.createCallContext(API_USER_NAME, CallOrigin.INTERNAL, UserType.SYSTEM);
 
         List<SubscriptionBundle> bundles = entitlementUserApi.getBundlesForAccount(accountId);
-        BillingEventSet result = new DefaultBillingEventSet();
+        DefaultBillingEventSet result = new DefaultBillingEventSet();
 
         try {
             Account account = accountApi.getAccountById(accountId);
-            
-
-            for (final SubscriptionBundle bundle: bundles) {
-                List<Subscription> subscriptions = entitlementUserApi.getSubscriptionsForBundle(bundle.getId());
-
-                for (final Subscription subscription: subscriptions) {
-                    for (final SubscriptionEvent transition : subscription.getBillingTransitions()) {
-                        try {
-                            int bcd = bcdCalculator.calculateBcd(bundle, subscription, transition, account);
-
-                            if(account.getBillCycleDay() == 0) {
-                                MutableAccountData modifiedData = account.toMutableAccountData();
-                                modifiedData.setBillCycleDay(bcd);
-                                accountApi.updateAccount(account.getExternalKey(), modifiedData, context);
-                            }
-
-                            BillingEvent event = new DefaultBillingEvent(account, transition, subscription, bcd, account.getCurrency(), catalogService.getFullCatalog());
-                            result.add(event);
-                        } catch (CatalogApiException e) {
-                            log.error("Failing to identify catalog components while creating BillingEvent from transition: " +
-                                    transition.getId().toString(), e);
-                        } catch (Exception e) {
-                            log.warn("Failed while getting BillingEvent", e);
-                        }
 
-                    }
-                }
+            // Check to see if billing is off for the account
+            Map<String,Tag> accountTags = tagApi.getTags(accountId,ObjectType.ACCOUNT);
+            if(accountTags.get(ControlTagType.AUTO_INVOICING_OFF.name()) != null) {
+                result.setAccountAutoInvoiceIsOff(true);
+                return result; // billing is off, we are done
             }
+
+            addBillingEventsForBundles(bundles, account, context, result);         
+           
         } catch (AccountApiException e) {
             log.warn("Failed while getting BillingEvent", e);
         }
+        
+        debugLog(result, "********* Billing Events Raw");
         blockCalculator.insertBlockingEvents(result);
+        debugLog(result,"*********  Billing Events After Blocking");
+        
         return result;
     }
+    
+
+    private void debugLog(SortedSet<BillingEvent> result, String title) {
+        log.debug(title);
+        Iterator<BillingEvent> i = result.iterator();
+        while(i.hasNext()) {
+            log.debug(i.next().toString());
+        }
+        
+    }
+
+    private void addBillingEventsForBundles(List<SubscriptionBundle> bundles, Account account, CallContext context,
+            DefaultBillingEventSet result) {
+        
+        for (final SubscriptionBundle bundle: bundles) {
+            List<Subscription> subscriptions = entitlementUserApi.getSubscriptionsForBundle(bundle.getId());
+
+            //Check if billing is off for the bundle
+            Map<String,Tag> bundleTags = tagApi.getTags(bundle.getId(), ObjectType.BUNDLE);
+            if(bundleTags.get(ControlTagType.AUTO_INVOICING_OFF.name()) != null) {
+                 for (final Subscription subscription: subscriptions) { // billing is off so list sub ids in set to be excluded
+                    result.getSubscriptionIdsWithAutoInvoiceOff().add(subscription.getId());
+                }
+            } else { // billing is not off
+                addBillingEventsForSubscription(subscriptions, bundle, account, context, result);                 
+            }
+        }        
+    }
+
+    private void addBillingEventsForSubscription(List<Subscription> subscriptions, SubscriptionBundle bundle, Account account, CallContext context, DefaultBillingEventSet result) {
+        for (final Subscription subscription: subscriptions) {
+            for (final SubscriptionEvent transition : subscription.getBillingTransitions()) {
+                try {
+                    int bcd = bcdCalculator.calculateBcd(bundle, subscription, transition, account);
 
+                    if(account.getBillCycleDay() == 0) {
+                        MutableAccountData modifiedData = account.toMutableAccountData();
+                        modifiedData.setBillCycleDay(bcd);
+                        accountApi.updateAccount(account.getExternalKey(), modifiedData, context);
+                    }
+
+                    BillingEvent event = new DefaultBillingEvent(account, transition, subscription, bcd, account.getCurrency(), catalogService.getFullCatalog());
+                    result.add(event);
+                } catch (CatalogApiException e) {
+                    log.error("Failing to identify catalog components while creating BillingEvent from transition: " +
+                            transition.getId().toString(), e);
+                } catch (Exception e) {
+                    log.warn("Failed while getting BillingEvent", e);
+                }
+
+            }
+        }
+    }
 
     @Override
     public UUID getAccountIdFromSubscriptionId(UUID subscriptionId) throws EntitlementBillingApiException {
diff --git a/junction/src/main/java/com/ning/billing/junction/plumbing/billing/DefaultBillingEventSet.java b/junction/src/main/java/com/ning/billing/junction/plumbing/billing/DefaultBillingEventSet.java
index fdda46c..d26f70b 100644
--- a/junction/src/main/java/com/ning/billing/junction/plumbing/billing/DefaultBillingEventSet.java
+++ b/junction/src/main/java/com/ning/billing/junction/plumbing/billing/DefaultBillingEventSet.java
@@ -55,5 +55,17 @@ public class DefaultBillingEventSet extends TreeSet<BillingEvent> implements Sor
         this.subscriptionIdsWithAutoInvoiceOff = subscriptionIdsWithAutoInvoiceOff;
     }
     
+    public boolean isLast(final BillingEvent event) {
+        return last() == event;
+    }
+
+    @Override
+    public String toString() {
+        return "DefaultBillingEventSet [accountAutoInvoiceOff=" + accountAutoInvoiceOff
+                + ", subscriptionIdsWithAutoInvoiceOff=" + subscriptionIdsWithAutoInvoiceOff + ", Events="
+                + super.toString() + "]";
+    }
+    
+    
     
 }
diff --git a/junction/src/test/java/com/ning/billing/junction/plumbing/billing/TestBlockingCalculator.java b/junction/src/test/java/com/ning/billing/junction/plumbing/billing/TestBlockingCalculator.java
index 435ee50..b501c98 100644
--- a/junction/src/test/java/com/ning/billing/junction/plumbing/billing/TestBlockingCalculator.java
+++ b/junction/src/test/java/com/ning/billing/junction/plumbing/billing/TestBlockingCalculator.java
@@ -556,7 +556,7 @@ public class TestBlockingCalculator {
         final BillingModeType billingModeType = BillingModeType.IN_ADVANCE;
         final BillingPeriod billingPeriod = BillingPeriod.MONTHLY;
         final SubscriptionTransitionType type = SubscriptionTransitionType.CHANGE;
-        final Long totalOrdering = 0L; //TODO
+        final Long totalOrdering = 0L; 
 
         return new DefaultBillingEvent(account, subscription, effectiveDate, plan, planPhase,
                 fixedPrice, recurringPrice, currency,
diff --git a/overdue/src/main/java/com/ning/billing/ovedue/notification/DefaultOverdueCheckNotifier.java b/overdue/src/main/java/com/ning/billing/ovedue/notification/DefaultOverdueCheckNotifier.java
index 9f45075..d13fdef 100644
--- a/overdue/src/main/java/com/ning/billing/ovedue/notification/DefaultOverdueCheckNotifier.java
+++ b/overdue/src/main/java/com/ning/billing/ovedue/notification/DefaultOverdueCheckNotifier.java
@@ -29,6 +29,7 @@ import com.ning.billing.overdue.listener.OverdueListener;
 import com.ning.billing.overdue.service.DefaultOverdueService;
 import com.ning.billing.util.notificationq.NotificationQueue;
 import com.ning.billing.util.notificationq.NotificationQueueService;
+import com.ning.billing.util.notificationq.NotificationQueueService.NoSuchNotificationQueue;
 import com.ning.billing.util.notificationq.NotificationQueueService.NotificationQueueAlreadyExists;
 import com.ning.billing.util.notificationq.NotificationQueueService.NotificationQueueHandler;
 
@@ -94,6 +95,11 @@ public class DefaultOverdueCheckNotifier implements  OverdueCheckNotifier {
     public void stop() {
         if (overdueQueue != null) {
         	overdueQueue.stopQueue();
+        	try {
+        	    notificationQueueService.deleteNotificationQueue(overdueQueue.getServiceName(), overdueQueue.getQueueName());
+        	} catch (NoSuchNotificationQueue e) {
+        	    log.error("Error deleting a queue by its own name - this should never happen", e);
+        	}
         }
     }
 
diff --git a/overdue/src/main/java/com/ning/billing/overdue/applicator/OverdueStateApplicator.java b/overdue/src/main/java/com/ning/billing/overdue/applicator/OverdueStateApplicator.java
index 958d8b9..cc0acb2 100644
--- a/overdue/src/main/java/com/ning/billing/overdue/applicator/OverdueStateApplicator.java
+++ b/overdue/src/main/java/com/ning/billing/overdue/applicator/OverdueStateApplicator.java
@@ -58,7 +58,7 @@ public class OverdueStateApplicator<T extends Blockable>{
             }
         } catch(OverdueApiException e) {
             if(e.getCode() != ErrorCode.OVERDUE_NO_REEVALUATION_INTERVAL.getCode()) {
-                new OverdueError(e);
+                throw new OverdueError(e);
             }
         }
 
diff --git a/overdue/src/main/java/com/ning/billing/overdue/wrapper/OverdueWrapperFactory.java b/overdue/src/main/java/com/ning/billing/overdue/wrapper/OverdueWrapperFactory.java
index 6d3b967..0abc240 100644
--- a/overdue/src/main/java/com/ning/billing/overdue/wrapper/OverdueWrapperFactory.java
+++ b/overdue/src/main/java/com/ning/billing/overdue/wrapper/OverdueWrapperFactory.java
@@ -46,7 +46,7 @@ public class OverdueWrapperFactory {
     private final OverdueStateApplicator<SubscriptionBundle> overdueStateApplicatorBundle;
     private final BlockingApi api;
     private final Clock clock;
-    private OverdueStateSet<SubscriptionBundle> overdueStates;
+    private OverdueConfig config;
 
     @Inject
     public OverdueWrapperFactory(BlockingApi api, Clock clock, 
@@ -64,7 +64,7 @@ public class OverdueWrapperFactory {
     public <T extends Blockable> OverdueWrapper<T> createOverdueWrapperFor(T bloackable) throws OverdueError {
   
         if(bloackable instanceof SubscriptionBundle) {
-            return (OverdueWrapper<T>)new OverdueWrapper<SubscriptionBundle>((SubscriptionBundle)bloackable, api, overdueStates, 
+            return (OverdueWrapper<T>)new OverdueWrapper<SubscriptionBundle>((SubscriptionBundle)bloackable, api, getOverdueStateSetBundle(), 
                     clock, billingStateCalcuatorBundle, overdueStateApplicatorBundle);
         } else {
             throw new OverdueError(ErrorCode.OVERDUE_TYPE_NOT_SUPPORTED, bloackable.getId(), bloackable.getClass());
@@ -79,7 +79,7 @@ public class OverdueWrapperFactory {
             switch (state.getType()) {
             case SUBSCRIPTION_BUNDLE : {
                 SubscriptionBundle bundle = entitlementApi.getBundleFromId(id);
-                return (OverdueWrapper<T>) new OverdueWrapper<SubscriptionBundle>(bundle, api, overdueStates, 
+                return (OverdueWrapper<T>) new OverdueWrapper<SubscriptionBundle>(bundle, api, getOverdueStateSetBundle(), 
                         clock, billingStateCalcuatorBundle, overdueStateApplicatorBundle );
             }
             default : {
@@ -92,11 +92,9 @@ public class OverdueWrapperFactory {
         }
     }
     
-    public void setOverdueConfig(OverdueConfig config) {
-        if(config != null) {
-            overdueStates = config.getBundleStateSet();
-        } else {
-            overdueStates = new DefaultOverdueStateSet<SubscriptionBundle>() {
+    private OverdueStateSet<SubscriptionBundle> getOverdueStateSetBundle() {
+        if(config == null || config.getBundleStateSet() == null) {
+            return new DefaultOverdueStateSet<SubscriptionBundle>() {
 
                 @SuppressWarnings("unchecked")
                 @Override
@@ -104,7 +102,13 @@ public class OverdueWrapperFactory {
                     return new DefaultOverdueState[0];
                 }
             };
+        } else {
+           return config.getBundleStateSet();
         }
     }
+    
+    public void setOverdueConfig(OverdueConfig config) {   
+        this.config = config;
+    }
 
 }
diff --git a/overdue/src/test/java/com/ning/billing/overdue/applicator/ApplicatorMockJunctionModule.java b/overdue/src/test/java/com/ning/billing/overdue/applicator/ApplicatorMockJunctionModule.java
index 25721d5..0dcf12e 100644
--- a/overdue/src/test/java/com/ning/billing/overdue/applicator/ApplicatorMockJunctionModule.java
+++ b/overdue/src/test/java/com/ning/billing/overdue/applicator/ApplicatorMockJunctionModule.java
@@ -48,43 +48,37 @@ public class ApplicatorMockJunctionModule extends MockJunctionModule {
 
                     @Override
                     public Type getType() {
-                        // TODO Auto-generated method stub
+
                         return null;
                     }
 
                     @Override
                     public DateTime getTimestamp() {
-                        // TODO Auto-generated method stub
                         return null;
                     }
 
                     @Override
                     public boolean isBlockChange() {
-                        // TODO Auto-generated method stub
                         return false;
                     }
 
                     @Override
                     public boolean isBlockEntitlement() {
-                        // TODO Auto-generated method stub
                         return false;
                     }
 
                     @Override
                     public boolean isBlockBilling() {
-                        // TODO Auto-generated method stub
                         return false;
                     }
 
                     @Override
                     public int compareTo(BlockingState arg0) {
-                        // TODO Auto-generated method stub
                         return 0;
                     }
 
                     @Override
                     public String getDescription() {
-                        // TODO Auto-generated method stub
                         return null;
                     }
                     
diff --git a/overdue/src/test/java/com/ning/billing/overdue/applicator/TestOverdueStateApplicator.java b/overdue/src/test/java/com/ning/billing/overdue/applicator/TestOverdueStateApplicator.java
index e95740a..ca6f682 100644
--- a/overdue/src/test/java/com/ning/billing/overdue/applicator/TestOverdueStateApplicator.java
+++ b/overdue/src/test/java/com/ning/billing/overdue/applicator/TestOverdueStateApplicator.java
@@ -49,7 +49,7 @@ public class TestOverdueStateApplicator extends OverdueTestBase {
     @Inject
     OverdueStateApplicator<SubscriptionBundle> applicator;
         
-    @Test( groups={"slow"} , enabled = false)
+    @Test( groups={"slow"} , enabled = true)
      public void testApplicator() throws Exception {
          InputStream is = new ByteArrayInputStream(configXml.getBytes());
          config = XMLLoader.getObjectFromStreamNoValidation(is,  OverdueConfig.class);
@@ -73,9 +73,6 @@ public class TestOverdueStateApplicator extends OverdueTestBase {
          applicator.apply(bundle, BlockingApi.CLEAR_STATE_NAME, state);
          checkStateApplied(state);
         
-         
-        //TODO
-        // Check notification is posted with correct time delay
     }
 
 
diff --git a/overdue/src/test/java/com/ning/billing/overdue/OverdueTestBase.java b/overdue/src/test/java/com/ning/billing/overdue/OverdueTestBase.java
index f233a9a..55163ca 100644
--- a/overdue/src/test/java/com/ning/billing/overdue/OverdueTestBase.java
+++ b/overdue/src/test/java/com/ning/billing/overdue/OverdueTestBase.java
@@ -22,11 +22,12 @@ import java.util.ArrayList;
 import java.util.List;
 import java.util.UUID;
 
+import javax.management.RuntimeErrorException;
+
 import org.apache.commons.io.IOUtils;
 import org.joda.time.DateTime;
 import org.testng.Assert;
 import org.testng.annotations.AfterClass;
-import org.testng.annotations.AfterMethod;
 import org.testng.annotations.BeforeClass;
 import org.testng.annotations.BeforeMethod;
 import org.testng.annotations.Guice;
@@ -50,12 +51,11 @@ import com.ning.billing.mock.BrainDeadProxyFactory;
 import com.ning.billing.mock.BrainDeadProxyFactory.ZombieControl;
 import com.ning.billing.mock.glue.MockClockModule;
 import com.ning.billing.mock.glue.MockInvoiceModule;
-import com.ning.billing.mock.glue.MockJunctionModule;
 import com.ning.billing.mock.glue.MockPaymentModule;
 import com.ning.billing.mock.glue.TestDbiModule;
 import com.ning.billing.overdue.applicator.ApplicatorMockJunctionModule;
-import com.ning.billing.overdue.applicator.TestOverdueStateApplicator;
 import com.ning.billing.overdue.applicator.ApplicatorMockJunctionModule.ApplicatorBlockingApi;
+import com.ning.billing.overdue.applicator.TestOverdueStateApplicator;
 import com.ning.billing.overdue.config.OverdueConfig;
 import com.ning.billing.overdue.glue.DefaultOverdueModule;
 import com.ning.billing.overdue.service.DefaultOverdueService;
@@ -64,6 +64,7 @@ import com.ning.billing.util.bus.BusService;
 import com.ning.billing.util.clock.ClockMock;
 import com.ning.billing.util.glue.BusModule;
 import com.ning.billing.util.glue.NotificationQueueModule;
+import com.ning.billing.util.notificationq.NotificationQueueService.NotificationQueueAlreadyExists;
 
 //@Guice(modules = {MockJunctionModule.class, MockInvoiceModule.class, DefaultOverdueModule.class})
 @Guice( modules = {DefaultOverdueModule.class, MockClockModule.class, ApplicatorMockJunctionModule.class, CatalogModule.class, MockInvoiceModule.class, MockPaymentModule.class, BusModule.class, NotificationQueueModule.class, TestDbiModule.class})
@@ -119,14 +120,14 @@ public class OverdueTestBase {
 
     @Inject
     protected BlockingApi blockingApi;
-    
+
     @Inject
     protected OverdueWrapperFactory overdueWrapperFactory;
-    
+
     @Inject
     protected OverdueUserApi overdueApi;
-   
-    
+
+
     @Inject
     protected InvoiceUserApi invoiceApi;
 
@@ -135,7 +136,7 @@ public class OverdueTestBase {
     protected String productName;
     protected BillingPeriod term;
     protected String planSetName;
- 
+
     @Inject
     EntitlementUserApi entitlementApi;
 
@@ -153,13 +154,19 @@ public class OverdueTestBase {
         helper.initDb(utilDdl);
     }
 
-  
+
     @BeforeClass(groups = "slow")
     public void setup() throws Exception{
 
         setupMySQL();
         service.registerForBus();
-        service.initialize();
+        try {
+            service.initialize();
+        }catch (RuntimeException e) {
+            if(!(e.getCause() instanceof NotificationQueueAlreadyExists)) {
+                throw e;
+            } 
+        }
         service.start();
     }
 
@@ -198,11 +205,12 @@ public class OverdueTestBase {
         UUID bundleId = UUID.randomUUID();
         ((ZombieControl)bundle).addResult("getId", bundleId);
         ((ZombieControl)bundle).addResult("getAccountId", UUID.randomUUID());
-        
+
         Invoice invoice = BrainDeadProxyFactory.createBrainDeadProxyFor(Invoice.class);
         ((ZombieControl)invoice).addResult("getInvoiceDate",dateOfLastUnPaidInvoice);
         ((ZombieControl)invoice).addResult("getBalance",BigDecimal.TEN);
         ((ZombieControl)invoice).addResult("getId",UUID.randomUUID());
+        ((ZombieControl)invoice).addResult("hashCode", UUID.randomUUID().hashCode());
 
         InvoiceItem item = BrainDeadProxyFactory.createBrainDeadProxyFor(InvoiceItem.class);
         ((ZombieControl)item).addResult("getBundleId",bundleId);
@@ -210,18 +218,18 @@ public class OverdueTestBase {
         items.add(item);
 
         ((ZombieControl)invoice).addResult("getInvoiceItems",items);
-        
+
         List<Invoice> invoices = new ArrayList<Invoice>();
         invoices.add(invoice);
         ((ZombieControl)invoiceApi).addResult("getUnpaidInvoicesByAccountId", invoices);
-        
-        
+
+
         Subscription base = BrainDeadProxyFactory.createBrainDeadProxyFor(Subscription.class);
         ((ZombieControl)base).addResult("getCurrentPlan", MockPlan.createBicycleNoTrialEvergreen1USD());
         ((ZombieControl)base).addResult("getCurrentPriceList", new MockPriceList());
         ((ZombieControl)base).addResult("getCurrentPhase", MockPlan.createBicycleNoTrialEvergreen1USD().getFinalPhase());
         ((ZombieControl)entitlementApi).addResult("getBaseSubscription", base);
-       
+
         return bundle;
     }
 }
diff --git a/payment/src/main/java/com/ning/billing/payment/api/DefaultPaymentApi.java b/payment/src/main/java/com/ning/billing/payment/api/DefaultPaymentApi.java
index 69c5cbf..fa52399 100644
--- a/payment/src/main/java/com/ning/billing/payment/api/DefaultPaymentApi.java
+++ b/payment/src/main/java/com/ning/billing/payment/api/DefaultPaymentApi.java
@@ -22,6 +22,7 @@ import java.util.Set;
 import java.util.UUID;
 
 import com.google.inject.Inject;
+import com.ning.billing.ErrorCode;
 import com.ning.billing.account.api.Account;
 import com.ning.billing.payment.core.AccountProcessor;
 import com.ning.billing.payment.core.PaymentMethodProcessor;
@@ -49,9 +50,6 @@ public class DefaultPaymentApi implements PaymentApi {
         this.refundProcessor = refundProcessor;
     }
      
- 
- 
-   
     @Override
     public Payment createPayment(final String accountKey, final UUID invoiceId, final BigDecimal amount, final CallContext context) 
     throws PaymentApiException {
@@ -64,6 +62,14 @@ public class DefaultPaymentApi implements PaymentApi {
         return paymentProcessor.createPayment(account, invoiceId, amount, context, true);        
     }
 
+    @Override
+    public Payment getPayment(UUID paymentId) throws PaymentApiException {
+        Payment payment = paymentProcessor.getPayment(paymentId);
+        if (payment == null) {
+            throw new PaymentApiException(ErrorCode.PAYMENT_NO_SUCH_PAYMENT, paymentId);
+        }
+        return payment;
+    }
     
     @Override
     public List<Payment> getInvoicePayments(UUID invoiceId) {
@@ -144,5 +150,4 @@ public class DefaultPaymentApi implements PaymentApi {
         throws PaymentApiException {
         methodProcessor.setDefaultPaymentMethod(account, paymentMethodId);
     }
-
 }
diff --git a/payment/src/main/java/com/ning/billing/payment/core/PaymentProcessor.java b/payment/src/main/java/com/ning/billing/payment/core/PaymentProcessor.java
index a2d2bd1..88ab7ad 100644
--- a/payment/src/main/java/com/ning/billing/payment/core/PaymentProcessor.java
+++ b/payment/src/main/java/com/ning/billing/payment/core/PaymentProcessor.java
@@ -19,6 +19,7 @@ import static com.ning.billing.payment.glue.PaymentModule.PLUGIN_EXECUTOR_NAMED;
 
 import java.math.BigDecimal;
 import java.math.RoundingMode;
+import java.util.Collections;
 import java.util.LinkedList;
 import java.util.List;
 import java.util.UUID;
@@ -101,7 +102,11 @@ public class PaymentProcessor extends ProcessorBase {
         this.voidPluginDispatcher = new PluginDispatcher<Void>(executor);
     }
   
+    public Payment getPayment(UUID paymentId) {
+        return getPayments(Collections.singletonList(paymentDao.getPayment(paymentId))).get(0);        
+    }
 
+    
     public List<Payment> getInvoicePayments(UUID invoiceId) {
         return getPayments(paymentDao.getPaymentsForInvoice(invoiceId));        
     }
diff --git a/payment/src/main/java/com/ning/billing/payment/dao/AuditedPaymentDao.java b/payment/src/main/java/com/ning/billing/payment/dao/AuditedPaymentDao.java
index 6ff1ae8..e8a49d4 100644
--- a/payment/src/main/java/com/ning/billing/payment/dao/AuditedPaymentDao.java
+++ b/payment/src/main/java/com/ning/billing/payment/dao/AuditedPaymentDao.java
@@ -19,6 +19,7 @@ import java.math.BigDecimal;
 import java.util.List;
 import java.util.UUID;
 
+
 import org.skife.jdbi.v2.IDBI;
 import org.skife.jdbi.v2.Transaction;
 import org.skife.jdbi.v2.TransactionStatus;
@@ -26,8 +27,11 @@ import org.skife.jdbi.v2.TransactionStatus;
 import com.google.common.base.Predicate;
 import com.google.common.collect.Collections2;
 import com.google.inject.Inject;
+import com.ning.billing.payment.api.PaymentApiException;
+import com.ning.billing.payment.api.PaymentInfoEvent;
 import com.ning.billing.payment.api.PaymentStatus;
 import com.ning.billing.payment.retry.PluginFailureRetryService.PluginFailureRetryServiceScheduler;
+
 import com.ning.billing.util.ChangeType;
 import com.ning.billing.util.callcontext.CallContext;
 import com.ning.billing.util.dao.EntityAudit;
@@ -273,13 +277,8 @@ public class AuditedPaymentDao implements PaymentDao {
         return paymentSqlDao.getPaymentsForAccount(accountId.toString());
     }
 
-
-
     @Override
     public List<PaymentAttemptModelDao> getAttemptsForPayment(UUID paymentId) {
         return paymentAttemptSqlDao.getPaymentAttempts(paymentId.toString());
     }
-
-
-
 }
diff --git a/payment/src/main/java/com/ning/billing/payment/dao/PaymentAttemptSqlDao.java b/payment/src/main/java/com/ning/billing/payment/dao/PaymentAttemptSqlDao.java
index 8932594..b1b53d6 100644
--- a/payment/src/main/java/com/ning/billing/payment/dao/PaymentAttemptSqlDao.java
+++ b/payment/src/main/java/com/ning/billing/payment/dao/PaymentAttemptSqlDao.java
@@ -15,12 +15,14 @@
  */
 package com.ning.billing.payment.dao;
 
+
 import java.math.BigDecimal;
 import java.sql.ResultSet;
 import java.sql.SQLException;
 import java.util.List;
 import java.util.UUID;
 
+
 import org.joda.time.DateTime;
 import org.skife.jdbi.v2.SQLStatement;
 import org.skife.jdbi.v2.StatementContext;
@@ -71,7 +73,6 @@ public interface PaymentAttemptSqlDao extends Transactional<PaymentAttemptSqlDao
     void insertHistoryFromTransaction(@PaymentAttemptHistoryBinder final EntityHistory<PaymentAttemptModelDao> payment,
                                             @CallContextBinder final CallContext context);
 
-
     public static final class PaymentAttemptModelDaoBinder extends BinderBase implements Binder<Bind, PaymentAttemptModelDao> {
         @Override
         public void bind(@SuppressWarnings("rawtypes") SQLStatement stmt, Bind bind, PaymentAttemptModelDao attempt) {
diff --git a/payment/src/main/java/com/ning/billing/payment/dao/PaymentSqlDao.java b/payment/src/main/java/com/ning/billing/payment/dao/PaymentSqlDao.java
index 11c4a8e..b0db533 100644
--- a/payment/src/main/java/com/ning/billing/payment/dao/PaymentSqlDao.java
+++ b/payment/src/main/java/com/ning/billing/payment/dao/PaymentSqlDao.java
@@ -76,6 +76,7 @@ public interface PaymentSqlDao extends Transactional<PaymentSqlDao>, UpdatableEn
                                             @CallContextBinder final CallContext context);
 
 
+
      public static final class PaymentModelDaoBinder extends BinderBase implements Binder<Bind, PaymentModelDao> {
         @Override
         public void bind(@SuppressWarnings("rawtypes") SQLStatement stmt, Bind bind, PaymentModelDao payment) {
@@ -98,7 +99,7 @@ public interface PaymentSqlDao extends Transactional<PaymentSqlDao>, UpdatableEn
             UUID id = getUUID(rs, "id");
             UUID accountId = getUUID(rs, "account_id");
             UUID invoiceId = getUUID(rs, "invoice_id");            
-            UUID paymentMethodId = null; //getUUID(rs, "payment_method_id");                        
+            UUID paymentMethodId = null; //getUUID(rs, "payment_method_id"); // STEPH needs to be fixed!                       
             Integer paymentNumber = rs.getInt("payment_number");
             BigDecimal amount = rs.getBigDecimal("amount");
             DateTime effectiveDate = getDate(rs, "effective_date");
@@ -109,3 +110,4 @@ public interface PaymentSqlDao extends Transactional<PaymentSqlDao>, UpdatableEn
         }
     }
 }
+
diff --git a/payment/src/main/java/com/ning/billing/payment/provider/DefaultPaymentProviderPluginRegistry.java b/payment/src/main/java/com/ning/billing/payment/provider/DefaultPaymentProviderPluginRegistry.java
index 3c7f9f0..c762e93 100644
--- a/payment/src/main/java/com/ning/billing/payment/provider/DefaultPaymentProviderPluginRegistry.java
+++ b/payment/src/main/java/com/ning/billing/payment/provider/DefaultPaymentProviderPluginRegistry.java
@@ -28,6 +28,7 @@ import com.ning.billing.payment.plugin.api.PaymentPluginApi;
 
 
 public class DefaultPaymentProviderPluginRegistry implements PaymentProviderPluginRegistry {
+    
     private final String defaultPlugin;
     private final Map<String, PaymentPluginApi> pluginsByName = new ConcurrentHashMap<String, PaymentPluginApi>();
 
diff --git a/payment/src/main/java/com/ning/billing/payment/provider/NoOpPaymentProviderPlugin.java b/payment/src/main/java/com/ning/billing/payment/provider/NoOpPaymentProviderPlugin.java
index b9605c2..63f11f6 100644
--- a/payment/src/main/java/com/ning/billing/payment/provider/NoOpPaymentProviderPlugin.java
+++ b/payment/src/main/java/com/ning/billing/payment/provider/NoOpPaymentProviderPlugin.java
@@ -45,8 +45,8 @@ public class NoOpPaymentProviderPlugin implements PaymentPluginApi {
     @Override
     public PaymentInfoPlugin processPayment(final String externalAccountKey, final UUID paymentId, final BigDecimal amount)
             throws PaymentPluginApiException {
+
         PaymentInfoPlugin paymentResult = new PaymentInfoPlugin() {
-        
             @Override
             public DateTime getEffectiveDate() {
                 return null;
diff --git a/payment/src/main/resources/com/ning/billing/payment/dao/PaymentAttemptSqlDao.sql.stg b/payment/src/main/resources/com/ning/billing/payment/dao/PaymentAttemptSqlDao.sql.stg
index 174b349..89b76b1 100644
--- a/payment/src/main/resources/com/ning/billing/payment/dao/PaymentAttemptSqlDao.sql.stg
+++ b/payment/src/main/resources/com/ning/billing/payment/dao/PaymentAttemptSqlDao.sql.stg
@@ -52,6 +52,12 @@ getRecordId() ::= <<
     WHERE id = :id;
 >>
 
+getPaymentAttemptIdFromPaymentId() ::= <<
+    SELECT id
+    FROM payment_attempts
+    WHERE payment_id = :paymentId;
+>>
+
 historyFields(prefix) ::= <<
     <prefix>record_id,
     <prefix>id,
diff --git a/payment/src/test/java/com/ning/billing/payment/MockInvoice.java b/payment/src/test/java/com/ning/billing/payment/MockInvoice.java
index d195b59..802b49a 100644
--- a/payment/src/test/java/com/ning/billing/payment/MockInvoice.java
+++ b/payment/src/test/java/com/ning/billing/payment/MockInvoice.java
@@ -23,6 +23,7 @@ import java.util.UUID;
 
 import javax.annotation.Nullable;
 
+import com.ning.billing.invoice.api.InvoiceItemType;
 import org.joda.time.DateTime;
 
 import com.ning.billing.catalog.api.Currency;
@@ -178,23 +179,37 @@ public class MockInvoice extends EntityBase implements Invoice {
     }
 
     @Override
-    public BigDecimal getTotalAmount() {
+    public BigDecimal getAmountCharged() {
         BigDecimal result = BigDecimal.ZERO;
     
         for(InvoiceItem i : invoiceItems) {
-            result = result.add(i.getAmount());
+            if (!i.getInvoiceItemType().equals(InvoiceItemType.CREDIT)) {
+                result = result.add(i.getAmount());
+            }
+        }
+        return result;
+    }
+
+    @Override
+    public BigDecimal getAmountCredited() {
+        BigDecimal result = BigDecimal.ZERO;
+
+        for(InvoiceItem i : invoiceItems) {
+            if (i.getInvoiceItemType().equals(InvoiceItemType.CREDIT)) {
+                result = result.add(i.getAmount());
+            }
         }
         return result;
     }
 
     @Override
     public BigDecimal getBalance() {
-        return getTotalAmount().subtract(getAmountPaid());
+        return getAmountCharged().subtract(getAmountPaid().subtract(getAmountCredited()));
     }
 
     @Override
     public boolean isDueForPayment(final DateTime targetDate, final int numberOfDays) {
-        if (getTotalAmount().compareTo(BigDecimal.ZERO) == 0) {
+        if (getBalance().compareTo(BigDecimal.ZERO) == 0) {
             return false;
         }
 
diff --git a/payment/src/test/java/com/ning/billing/payment/MockRecurringInvoiceItem.java b/payment/src/test/java/com/ning/billing/payment/MockRecurringInvoiceItem.java
index 93c90fa..e55c7e4 100644
--- a/payment/src/test/java/com/ning/billing/payment/MockRecurringInvoiceItem.java
+++ b/payment/src/test/java/com/ning/billing/payment/MockRecurringInvoiceItem.java
@@ -18,6 +18,7 @@ package com.ning.billing.payment;
 
 import com.ning.billing.catalog.api.Currency;
 import com.ning.billing.invoice.api.InvoiceItem;
+import com.ning.billing.invoice.api.InvoiceItemType;
 import com.ning.billing.util.entity.EntityBase;
 
 import org.apache.commons.lang.NotImplementedException;
@@ -151,6 +152,12 @@ public class MockRecurringInvoiceItem extends EntityBase implements InvoiceItem 
     public Currency getCurrency() {
         return currency;
     }
+
+    @Override
+    public InvoiceItemType getInvoiceItemType() {
+        return InvoiceItemType.RECURRING;
+    }
+
     @Override
     public InvoiceItem asReversingItem() {
         throw new NotImplementedException();

pom.xml 20(+13 -7)

diff --git a/pom.xml b/pom.xml
index 2801091..f516c7a 100644
--- a/pom.xml
+++ b/pom.xml
@@ -1,11 +1,11 @@
 <!-- ~ Copyright 2010-2011 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. -->
+	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. -->
 
 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
     <parent>
@@ -344,6 +344,12 @@
                 <scope>test</scope>
             </dependency>
             <dependency>
+                <groupId>org.mockito</groupId>
+                <artifactId>mockito-all</artifactId>
+                <version>1.9.0</version>
+                <scope>test</scope>
+            </dependency>
+            <dependency>
                 <groupId>com.samskivert</groupId>
                 <artifactId>jmustache</artifactId>
                 <version>1.5</version>
diff --git a/server/src/main/java/com/ning/billing/server/modules/KillbillServerModule.java b/server/src/main/java/com/ning/billing/server/modules/KillbillServerModule.java
index b21195f..68b2f28 100644
--- a/server/src/main/java/com/ning/billing/server/modules/KillbillServerModule.java
+++ b/server/src/main/java/com/ning/billing/server/modules/KillbillServerModule.java
@@ -16,9 +16,6 @@
 
 package com.ning.billing.server.modules;
 
-import com.ning.billing.util.email.EmailModule;
-import com.ning.billing.util.email.templates.TemplateModule;
-import com.ning.billing.util.glue.GlobalLockerModule;
 import org.skife.jdbi.v2.DBI;
 import org.skife.jdbi.v2.IDBI;
 
@@ -31,17 +28,22 @@ import com.ning.billing.entitlement.glue.DefaultEntitlementModule;
 import com.ning.billing.invoice.glue.DefaultInvoiceModule;
 import com.ning.billing.jaxrs.resources.AccountResource;
 import com.ning.billing.jaxrs.resources.BundleResource;
+import com.ning.billing.jaxrs.resources.CatalogResource;
 import com.ning.billing.jaxrs.resources.InvoiceResource;
 import com.ning.billing.jaxrs.resources.SubscriptionResource;
 import com.ning.billing.jaxrs.resources.TagResource;
 import com.ning.billing.jaxrs.util.KillbillEventHandler;
 import com.ning.billing.jaxrs.util.TagHelper;
 import com.ning.billing.junction.glue.DefaultJunctionModule;
+
 import com.ning.billing.payment.glue.PaymentModule;
+import com.ning.billing.util.email.EmailModule;
+import com.ning.billing.util.email.templates.TemplateModule;
 import com.ning.billing.util.glue.BusModule;
 import com.ning.billing.util.glue.CallContextModule;
 import com.ning.billing.util.glue.ClockModule;
 import com.ning.billing.util.glue.CustomFieldModule;
+import com.ning.billing.util.glue.GlobalLockerModule;
 import com.ning.billing.util.glue.NotificationQueueModule;
 import com.ning.billing.util.glue.TagStoreModule;
 import com.ning.jetty.jdbi.guice.providers.DBIProvider;
@@ -67,6 +69,7 @@ public class KillbillServerModule extends AbstractModule
         bind(SubscriptionResource.class).asEagerSingleton();
         bind(InvoiceResource.class).asEagerSingleton();
         bind(TagResource.class).asEagerSingleton();
+        bind(CatalogResource.class).asEagerSingleton();
         bind(KillbillEventHandler.class).asEagerSingleton();
     }
 
diff --git a/server/src/test/java/com/ning/billing/jaxrs/TestJaxrsBase.java b/server/src/test/java/com/ning/billing/jaxrs/TestJaxrsBase.java
index cc78462..ee4cd87 100644
--- a/server/src/test/java/com/ning/billing/jaxrs/TestJaxrsBase.java
+++ b/server/src/test/java/com/ning/billing/jaxrs/TestJaxrsBase.java
@@ -111,7 +111,7 @@ public class TestJaxrsBase {
     protected ClockMock clock;
     protected TestApiListener busHandler;
 
-    // Context informtation to be passed around
+    // Context information to be passed around
     private static final String createdBy = "Toto";
     private static final String reason = "i am god";
     private static final String comment = "no comment";    
diff --git a/server/src/test/java/com/ning/billing/jaxrs/TestTag.java b/server/src/test/java/com/ning/billing/jaxrs/TestTag.java
index da8318f..6248357 100644
--- a/server/src/test/java/com/ning/billing/jaxrs/TestTag.java
+++ b/server/src/test/java/com/ning/billing/jaxrs/TestTag.java
@@ -23,8 +23,6 @@ import java.util.List;
 import javax.ws.rs.core.Response.Status;
 
 import com.ning.billing.jaxrs.resources.JaxrsResource;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
 import org.testng.annotations.Test;
 
 import com.fasterxml.jackson.core.type.TypeReference;
@@ -32,13 +30,10 @@ import com.ning.billing.jaxrs.json.TagDefinitionJson;
 import com.ning.http.client.Response;
 
 public class TestTag extends TestJaxrsBase {
-
-    private static final Logger log = LoggerFactory.getLogger(TestTag.class);
-
     @Test(groups="slow", enabled=true)
     public void testTagDefinitionOk() throws Exception {
     
-        TagDefinitionJson input = new TagDefinitionJson("blue", "realxing color");
+        TagDefinitionJson input = new TagDefinitionJson("blue", "relaxing color");
         String baseJson = mapper.writeValueAsString(input);
         Response response = doPost(JaxrsResource.TAG_DEFINITIONS_PATH, baseJson, DEFAULT_EMPTY_QUERY, DEFAULT_HTTP_TIMEOUT_SEC);
         assertEquals(response.getStatusCode(), Status.CREATED.getStatusCode());
@@ -66,7 +61,7 @@ public class TestTag extends TestJaxrsBase {
         List<TagDefinitionJson> objFromJson = mapper.readValue(baseJson, new TypeReference<List<TagDefinitionJson>>() {});
         int sizeSystemTag = (objFromJson == null || objFromJson.size() == 0) ? 0 : objFromJson.size();
         
-        TagDefinitionJson input = new TagDefinitionJson("blue", "realxing color");
+        TagDefinitionJson input = new TagDefinitionJson("blue", "relaxing color");
         baseJson = mapper.writeValueAsString(input);
         response = doPost(JaxrsResource.TAG_DEFINITIONS_PATH, baseJson, DEFAULT_EMPTY_QUERY, DEFAULT_HTTP_TIMEOUT_SEC);
         assertEquals(response.getStatusCode(), Status.CREATED.getStatusCode());
@@ -81,7 +76,7 @@ public class TestTag extends TestJaxrsBase {
         response = doPost(JaxrsResource.TAG_DEFINITIONS_PATH, baseJson, DEFAULT_EMPTY_QUERY, DEFAULT_HTTP_TIMEOUT_SEC);
         assertEquals(response.getStatusCode(), Status.CREATED.getStatusCode());
 
-        input = new TagDefinitionJson("green", "super realxing color");
+        input = new TagDefinitionJson("green", "super relaxing color");
         baseJson = mapper.writeValueAsString(input);
         response = doPost(JaxrsResource.TAG_DEFINITIONS_PATH, baseJson, DEFAULT_EMPTY_QUERY, DEFAULT_HTTP_TIMEOUT_SEC);
         assertEquals(response.getStatusCode(), Status.CREATED.getStatusCode());
diff --git a/util/src/main/java/com/ning/billing/util/callcontext/CallContextFactory.java b/util/src/main/java/com/ning/billing/util/callcontext/CallContextFactory.java
index d9f18c9..3194c02 100644
--- a/util/src/main/java/com/ning/billing/util/callcontext/CallContextFactory.java
+++ b/util/src/main/java/com/ning/billing/util/callcontext/CallContextFactory.java
@@ -22,7 +22,10 @@ import org.joda.time.DateTime;
 
 public interface CallContextFactory {
     CallContext createCallContext(String userName, CallOrigin callOrigin, UserType userType, UUID userToken);
-    
+
+    CallContext createCallContext(String userName, CallOrigin callOrigin, UserType userType,
+                                  String reasonCode, String comment, UUID userToken);
+
     CallContext createCallContext(String userName, CallOrigin callOrigin, UserType userType);    
 
     CallContext createMigrationCallContext(String userName, CallOrigin callOrigin, UserType userType, DateTime createdDate, DateTime updatedDate);
diff --git a/util/src/main/java/com/ning/billing/util/callcontext/DefaultCallContext.java b/util/src/main/java/com/ning/billing/util/callcontext/DefaultCallContext.java
index 4cde143..4bb7c24 100644
--- a/util/src/main/java/com/ning/billing/util/callcontext/DefaultCallContext.java
+++ b/util/src/main/java/com/ning/billing/util/callcontext/DefaultCallContext.java
@@ -22,7 +22,6 @@ import com.ning.billing.util.clock.Clock;
 import org.joda.time.DateTime;
 
 public class DefaultCallContext extends CallContextBase {
-	
     private final Clock clock;
 
     public DefaultCallContext(final String userName, final CallOrigin callOrigin, final UserType userType, final UUID userToken, final Clock clock) {
@@ -30,6 +29,13 @@ public class DefaultCallContext extends CallContextBase {
         this.clock = clock;
     }
 
+    public DefaultCallContext(final String userName, final CallOrigin callOrigin, final UserType userType,
+                              final String reasonCode, final String comment,
+                              final UUID userToken, final Clock clock) {
+        super(userName, callOrigin, userType, reasonCode, comment, userToken);
+        this.clock = clock;
+    }
+
     public DefaultCallContext(String userName, CallOrigin callOrigin, UserType userType, Clock clock) {
     	this(userName, callOrigin, userType, null, clock);
     }
diff --git a/util/src/main/java/com/ning/billing/util/callcontext/DefaultCallContextFactory.java b/util/src/main/java/com/ning/billing/util/callcontext/DefaultCallContextFactory.java
index 52ac313..53b5ba1 100644
--- a/util/src/main/java/com/ning/billing/util/callcontext/DefaultCallContextFactory.java
+++ b/util/src/main/java/com/ning/billing/util/callcontext/DefaultCallContextFactory.java
@@ -22,6 +22,8 @@ import com.google.inject.Inject;
 import com.ning.billing.util.clock.Clock;
 import org.joda.time.DateTime;
 
+import javax.annotation.Nullable;
+
 public class DefaultCallContextFactory implements CallContextFactory {
     private final Clock clock;
 
@@ -31,11 +33,18 @@ public class DefaultCallContextFactory implements CallContextFactory {
     }
 
     @Override
-    public CallContext createCallContext(String userName, CallOrigin callOrigin, UserType userType, UUID userToken) {
+    public CallContext createCallContext(String userName, CallOrigin callOrigin, UserType userType,
+                                         @Nullable UUID userToken) {
         return new DefaultCallContext(userName, callOrigin, userType, userToken, clock);
     }
 
     @Override
+    public CallContext createCallContext(String userName, CallOrigin callOrigin, UserType userType,
+                                         String reasonCode, String comment, UUID userToken) {
+        return new DefaultCallContext(userName, callOrigin, userType, reasonCode, comment, userToken, clock);
+    }
+
+    @Override
     public CallContext createCallContext(String userName, CallOrigin callOrigin, UserType userType) {
     	return createCallContext(userName, callOrigin, userType, null);
     }
diff --git a/util/src/main/java/com/ning/billing/util/dao/AuditedCollectionDaoBase.java b/util/src/main/java/com/ning/billing/util/dao/AuditedCollectionDaoBase.java
index ba7084c..6c09c85 100644
--- a/util/src/main/java/com/ning/billing/util/dao/AuditedCollectionDaoBase.java
+++ b/util/src/main/java/com/ning/billing/util/dao/AuditedCollectionDaoBase.java
@@ -53,7 +53,7 @@ public abstract class AuditedCollectionDaoBase<T extends Entity> implements Audi
                     entityIterator.remove();
                     existingEntityIterator.remove();
 
-                    // if the entities have the same hashcode (e.g. same data), don't bother updating
+                    // if the entities have the same hash code (e.g. same data), don't bother updating
                     if (entity.hashCode() != existingEntity.hashCode()) {
                         entitiesToUpdate.add(entity);
                     }
diff --git a/util/src/main/java/com/ning/billing/util/dao/BinderBase.java b/util/src/main/java/com/ning/billing/util/dao/BinderBase.java
index ef20575..df36442 100644
--- a/util/src/main/java/com/ning/billing/util/dao/BinderBase.java
+++ b/util/src/main/java/com/ning/billing/util/dao/BinderBase.java
@@ -19,9 +19,18 @@ package com.ning.billing.util.dao;
 import org.joda.time.DateTime;
 
 import java.util.Date;
+import java.util.UUID;
 
 public abstract class BinderBase {
     protected Date getDate(DateTime dateTime) {
         return dateTime == null ? null : dateTime.toDate();
     }
+
+    protected String uuidToString(UUID uuid) {
+        if (uuid == null) {
+            return null;
+        } else {
+            return uuid.toString();
+        }
+    }
 }
diff --git a/util/src/main/java/com/ning/billing/util/dao/TableName.java b/util/src/main/java/com/ning/billing/util/dao/TableName.java
index 28fa045..98ec80d 100644
--- a/util/src/main/java/com/ning/billing/util/dao/TableName.java
+++ b/util/src/main/java/com/ning/billing/util/dao/TableName.java
@@ -21,6 +21,7 @@ public enum TableName {
     ACCOUNT_HISTORY("account_history"),
     ACCOUNT_EMAIL_HISTORY("account_email_history"),
     BUNDLES("bundles"),
+    CREDIT_INVOICE_ITEMS("credit_invoice_items"),
     CUSTOM_FIELD_HISTORY("custom_field_history"),
     FIXED_INVOICE_ITEMS("fixed_invoice_items"),
     INVOICE_PAYMENTS("invoice_payments"),
diff --git a/util/src/main/java/com/ning/billing/util/entity/collection/dao/EntityCollectionSqlDao.java b/util/src/main/java/com/ning/billing/util/entity/collection/dao/EntityCollectionSqlDao.java
index 6fe2ec6..475ae8d 100644
--- a/util/src/main/java/com/ning/billing/util/entity/collection/dao/EntityCollectionSqlDao.java
+++ b/util/src/main/java/com/ning/billing/util/entity/collection/dao/EntityCollectionSqlDao.java
@@ -58,7 +58,7 @@ public interface EntityCollectionSqlDao<T extends Entity> {
     @RegisterMapper(RecordIdMapper.class)
     @SqlQuery
     public List<Mapper<UUID, Long>> getRecordIds(@Bind("objectId") final String objectId,
-                                                @ObjectTypeBinder final ObjectType objectType);
+                                                 @ObjectTypeBinder final ObjectType objectType);
 
     @SqlUpdate
     public void test();
diff --git a/util/src/main/java/com/ning/billing/util/tag/api/DefaultTagUserApi.java b/util/src/main/java/com/ning/billing/util/tag/api/DefaultTagUserApi.java
index 9b67ede..b988bde 100644
--- a/util/src/main/java/com/ning/billing/util/tag/api/DefaultTagUserApi.java
+++ b/util/src/main/java/com/ning/billing/util/tag/api/DefaultTagUserApi.java
@@ -20,6 +20,7 @@ import java.util.List;
 import java.util.Map;
 import java.util.UUID;
 
+import com.ning.billing.invoice.api.InvoiceApiException;
 import com.ning.billing.util.dao.ObjectType;
 import com.ning.billing.util.tag.dao.TagDao;
 
@@ -84,7 +85,7 @@ public class DefaultTagUserApi implements TagUserApi {
     }
 
     @Override
-    public void removeTags(UUID objectId, ObjectType objectType, List<TagDefinition> tagDefinitions, CallContext context) {
+    public void removeTags(UUID objectId, ObjectType objectType, List<TagDefinition> tagDefinitions, CallContext context)  {
         // TODO: consider making this batch
         for (TagDefinition tagDefinition : tagDefinitions) {
             tagDao.deleteTag(objectId, objectType, tagDefinition, context);
diff --git a/util/src/main/java/com/ning/billing/util/tag/dao/DefaultTagDefinitionDao.java b/util/src/main/java/com/ning/billing/util/tag/dao/DefaultTagDefinitionDao.java
index 1b6ce2a..39970da 100644
--- a/util/src/main/java/com/ning/billing/util/tag/dao/DefaultTagDefinitionDao.java
+++ b/util/src/main/java/com/ning/billing/util/tag/dao/DefaultTagDefinitionDao.java
@@ -52,6 +52,12 @@ public class DefaultTagDefinitionDao implements TagDefinitionDao {
 
     @Override
     public TagDefinition getByName(final String definitionName) {
+        // add control tag definitions
+        for (ControlTagType controlTag : ControlTagType.values()) {
+            if(definitionName.equals(controlTag.name())) {
+                return new DefaultTagDefinition(controlTag.toString(), controlTag.getDescription(), true);
+            }
+        }
         return dao.getByName(definitionName);
     }
 
diff --git a/util/src/main/java/com/ning/billing/util/tag/dao/TagDao.java b/util/src/main/java/com/ning/billing/util/tag/dao/TagDao.java
index 91cac4d..4e29fc2 100644
--- a/util/src/main/java/com/ning/billing/util/tag/dao/TagDao.java
+++ b/util/src/main/java/com/ning/billing/util/tag/dao/TagDao.java
@@ -16,6 +16,7 @@
 
 package com.ning.billing.util.tag.dao;
 
+import com.ning.billing.invoice.api.InvoiceApiException;
 import com.ning.billing.util.callcontext.CallContext;
 import com.ning.billing.util.dao.AuditedCollectionDao;
 import com.ning.billing.util.dao.ObjectType;
diff --git a/util/src/main/java/com/ning/billing/util/tag/dao/TagMapper.java b/util/src/main/java/com/ning/billing/util/tag/dao/TagMapper.java
index 986e49d..bf08dcd 100644
--- a/util/src/main/java/com/ning/billing/util/tag/dao/TagMapper.java
+++ b/util/src/main/java/com/ning/billing/util/tag/dao/TagMapper.java
@@ -42,12 +42,11 @@ public class TagMapper extends MapperBase implements ResultSetMapper<Tag> {
             }
         }
 
+        UUID id = UUID.fromString(result.getString("id"));
         if (thisTagType == null) {
-            UUID id = UUID.fromString(result.getString("id"));
-
             return new DescriptiveTag(id, name);
         } else {
-            return new DefaultControlTag(thisTagType);
+            return new DefaultControlTag(id, thisTagType);
         }
     }
 }
diff --git a/util/src/main/resources/com/ning/billing/util/email/templates/HtmlInvoiceTemplate.mustache b/util/src/main/resources/com/ning/billing/util/email/templates/HtmlInvoiceTemplate.mustache
index 73c70b3..aef1dce 100644
--- a/util/src/main/resources/com/ning/billing/util/email/templates/HtmlInvoiceTemplate.mustache
+++ b/util/src/main/resources/com/ning/billing/util/email/templates/HtmlInvoiceTemplate.mustache
@@ -78,7 +78,7 @@
             <tr>
                 <td colspan=2 />
                 <td align=right><strong>{{text.invoiceAmount}}</strong></td>
-                <td align=right><strong>{{invoice.totalAmount}}</strong></td>
+                <td align=right><strong>{{invoice.amountCharged}}</strong></td>
             </tr>
             <tr>
                 <td colspan=2 />
diff --git a/util/src/test/java/com/ning/billing/util/email/DefaultCatalogTranslationTest.java b/util/src/test/java/com/ning/billing/util/email/DefaultCatalogTranslationTest.java
index 0be05ac..a42829f 100644
--- a/util/src/test/java/com/ning/billing/util/email/DefaultCatalogTranslationTest.java
+++ b/util/src/test/java/com/ning/billing/util/email/DefaultCatalogTranslationTest.java
@@ -35,7 +35,7 @@ public class DefaultCatalogTranslationTest {
         translation = new DefaultCatalogTranslator(config);
     }
 
-    @Test(groups = {"fast", "email"})
+    @Test(groups = {"fast", "email"}, enabled = false)
     public void testInitialization() {
         String ningPlusText = "ning-plus";
         String ningProText = "ning-pro";
@@ -54,7 +54,7 @@ public class DefaultCatalogTranslationTest {
         assertEquals(translation.getTranslation(Locale.CHINA, badText), badText);
     }
 
-    @Test
+    @Test(enabled = false)
     public void testExistingTranslation() {
         // if the translation exists, return the translation
         String originalText = "ning-plus";
@@ -68,7 +68,7 @@ public class DefaultCatalogTranslationTest {
         assertEquals(translation.getTranslation(Locale.US, originalText), originalText);
     }
 
-    @Test
+    @Test(enabled = false)
     public void testMissingTranslationFileWithEnglishText() {
         // if the translation file doesn't exist, return the "English" translation
         String originalText = "ning-plus";
diff --git a/util/src/test/java/com/ning/billing/util/tag/dao/MockTagDao.java b/util/src/test/java/com/ning/billing/util/tag/dao/MockTagDao.java
index f37b636..e67ef75 100644
--- a/util/src/test/java/com/ning/billing/util/tag/dao/MockTagDao.java
+++ b/util/src/test/java/com/ning/billing/util/tag/dao/MockTagDao.java
@@ -22,6 +22,7 @@ import com.ning.billing.util.tag.Tag;
 import com.ning.billing.util.tag.TagDefinition;
 import org.skife.jdbi.v2.sqlobject.mixins.Transmogrifier;
 
+import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.Iterator;
 import java.util.List;
@@ -77,6 +78,10 @@ public class MockTagDao implements TagDao {
             }
         };
 
+        if (tagStore.get(objectId) == null) {
+            tagStore.put(objectId, new ArrayList<Tag>());
+        }
+
         tagStore.get(objectId).add(tag);
     }
 
diff --git a/util/src/test/java/com/ning/billing/util/tag/TestTagStore.java b/util/src/test/java/com/ning/billing/util/tag/TestTagStore.java
index 1bded44..b452203 100644
--- a/util/src/test/java/com/ning/billing/util/tag/TestTagStore.java
+++ b/util/src/test/java/com/ning/billing/util/tag/TestTagStore.java
@@ -22,6 +22,7 @@ import java.util.List;
 import java.util.Map;
 import java.util.UUID;
 
+import com.ning.billing.invoice.api.InvoiceApiException;
 import com.ning.billing.util.callcontext.CallContext;
 import com.ning.billing.util.callcontext.CallOrigin;
 import com.ning.billing.util.callcontext.UserType;
@@ -417,7 +418,7 @@ public class TestTagStore {
 
         Handle handle = dbi.open();
         String query = String.format("select * from audit_log a inner join tag_history th on a.record_id = th.history_record_id where a.table_name = 'tag_history' and th.id='%s' and a.change_type='DELETE'",
-                                     tag.getId().toString());
+                tag.getId().toString());
         List<Map<String, Object>> result = handle.select(query);
         handle.close();
 
@@ -440,7 +441,7 @@ public class TestTagStore {
     }
 
     @Test
-    public void testRemoveTag() {
+    public void testRemoveTag() throws InvoiceApiException {
         UUID objectId = UUID.randomUUID();
         ObjectType objectType = ObjectType.INVOICE;
         TagDefinition tagDefinition = new DefaultTagDefinition("test tag", "test", false);