killbill-uncached

Changes

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

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

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

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

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

invoice/src/main/resources/com/ning/billing/invoice/dao/InvoiceDao.sql.stg 56(+0 -56)

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

pom.xml 2(+1 -1)

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

Details

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

diff --git a/account/pom.xml b/account/pom.xml
index c4aad73..01b61f9 100644
--- a/account/pom.xml
+++ b/account/pom.xml
@@ -13,7 +13,7 @@
     <parent>
         <groupId>com.ning.billing</groupId>
         <artifactId>killbill</artifactId>
-        <version>0.0.18-SNAPSHOT2</version>
+        <version>0.0.18-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>killbill-account</artifactId>
diff --git a/account/src/main/java/com/ning/billing/account/api/user/DefaultAccountChangeNotification.java b/account/src/main/java/com/ning/billing/account/api/user/DefaultAccountChangeNotification.java
index 43ade0f..6bcc634 100644
--- a/account/src/main/java/com/ning/billing/account/api/user/DefaultAccountChangeNotification.java
+++ b/account/src/main/java/com/ning/billing/account/api/user/DefaultAccountChangeNotification.java
@@ -71,8 +71,17 @@ public class DefaultAccountChangeNotification implements AccountChangeNotificati
             changedFields.add(new DefaultChangedField("billCycleDay", Integer.toString(oldData.getBillCycleDay()),
                                                                Integer.toString(newData.getBillCycleDay())));
         }
-        if (!newData.getPaymentProviderName().equals(oldData.getPaymentProviderName())) {
-            changedFields.add((new DefaultChangedField("paymentProviderName", oldData.getPaymentProviderName(), newData.getPaymentProviderName())));
+
+        String oldProviderName = oldData.getPaymentProviderName();
+        String newProviderName = newData.getPaymentProviderName();
+
+        if ((newProviderName == null) && (oldProviderName == null)) {
+        } else if ((newProviderName == null) && (oldProviderName != null)) {
+            changedFields.add((new DefaultChangedField("paymentProviderName", oldProviderName, newProviderName)));
+        } else if ((newProviderName != null) && (oldProviderName == null)) {
+            changedFields.add((new DefaultChangedField("paymentProviderName", oldProviderName, newProviderName)));
+        } else if (!newProviderName.equals(oldProviderName)) {
+            changedFields.add((new DefaultChangedField("paymentProviderName", oldProviderName, newProviderName)));
         }
 
         return changedFields;
diff --git a/account/src/main/java/com/ning/billing/account/glue/AccountModule.java b/account/src/main/java/com/ning/billing/account/glue/AccountModule.java
index d70548d..275d4c9 100644
--- a/account/src/main/java/com/ning/billing/account/glue/AccountModule.java
+++ b/account/src/main/java/com/ning/billing/account/glue/AccountModule.java
@@ -32,8 +32,6 @@ public class AccountModule extends AbstractModule {
     }
 
     private void installAccountCore() {
-//        bind(IAccountService.class).to(Engine.class).asEagerSingleton();
-//        bind(Engine.class).asEagerSingleton();
     }
 
     private void installAccountDao() {
diff --git a/account/src/test/java/com/ning/billing/account/dao/AccountDaoTestBase.java b/account/src/test/java/com/ning/billing/account/dao/AccountDaoTestBase.java
index ce9570f..c565e59 100644
--- a/account/src/test/java/com/ning/billing/account/dao/AccountDaoTestBase.java
+++ b/account/src/test/java/com/ning/billing/account/dao/AccountDaoTestBase.java
@@ -44,10 +44,6 @@ public abstract class AccountDaoTestBase {
             module.createDb(ddl);
 
             final Injector injector = Guice.createInjector(Stage.DEVELOPMENT, module);
-
-            //fieldStoreDao = injector.getInstance(FieldStoreDao.class);
-            //fieldStoreDao.test();
-
             dbi = injector.getInstance(IDBI.class);
 
             accountDao = injector.getInstance(AccountDao.class);
diff --git a/account/src/test/java/com/ning/billing/account/dao/TestSimpleAccountDao.java b/account/src/test/java/com/ning/billing/account/dao/TestSimpleAccountDao.java
index c024f22..7a82f98 100644
--- a/account/src/test/java/com/ning/billing/account/dao/TestSimpleAccountDao.java
+++ b/account/src/test/java/com/ning/billing/account/dao/TestSimpleAccountDao.java
@@ -47,9 +47,10 @@ public class TestSimpleAccountDao extends AccountDaoTestBase {
         String lastName = UUID.randomUUID().toString();
         String thisEmail = email + " " + UUID.randomUUID();
         String name = firstName + " " + lastName;
+        String phone = "123-456-7890";
 
         int firstNameLength = firstName.length();
-        return new AccountBuilder().externalKey(thisKey).name(name).firstNameLength(firstNameLength)
+        return new AccountBuilder().externalKey(thisKey).name(name).phone(phone).firstNameLength(firstNameLength)
                                    .email(thisEmail).currency(Currency.USD).build();
     }
 
diff --git a/analytics/pom.xml b/analytics/pom.xml
index 78da905..692fded 100644
--- a/analytics/pom.xml
+++ b/analytics/pom.xml
@@ -13,7 +13,7 @@
     <parent>
         <groupId>com.ning.billing</groupId>
         <artifactId>killbill</artifactId>
-        <version>0.0.18-SNAPSHOT2</version>
+        <version>0.0.18-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>killbill-analytics</artifactId>

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

diff --git a/api/pom.xml b/api/pom.xml
index 8ec5231..013feb9 100644
--- a/api/pom.xml
+++ b/api/pom.xml
@@ -13,7 +13,7 @@
     <parent>
         <groupId>com.ning.billing</groupId>
         <artifactId>killbill</artifactId>
-        <version>0.0.18-SNAPSHOT2</version>
+        <version>0.0.18-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>killbill-api</artifactId>
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 6e9f57f..bde12a9 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
@@ -21,9 +21,12 @@ import com.ning.billing.catalog.api.Currency;
 import java.math.BigDecimal;
 import java.util.List;
 import java.util.UUID;
+import org.joda.time.DateTime;
 
 public interface InvoicePaymentApi {
-    public void paymentSuccessful(UUID invoiceId, BigDecimal amount, Currency currency, UUID paymentId);
+    public void paymentSuccessful(UUID invoiceId, BigDecimal amount, Currency currency, UUID paymentId, DateTime paymentAttemptDate);
+
+    public void paymentFailed(UUID invoiceId, UUID paymentId, DateTime paymentAttemptDate);
 
     public List<Invoice> getInvoicesByAccount(UUID accountId);
 
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 a32ba01..2c8d02e 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
@@ -21,6 +21,7 @@ import org.joda.time.DateTime;
 import java.math.BigDecimal;
 import java.util.List;
 import java.util.UUID;
+import com.ning.billing.catalog.api.Currency;
 
 public interface InvoiceUserApi {
     public List<UUID> getInvoicesForPayment(DateTime targetDate, int numberOfDays);
@@ -29,7 +30,8 @@ public interface InvoiceUserApi {
 
     public Invoice getInvoice(UUID invoiceId);
 
-    public void paymentAttemptFailed(UUID invoiceId, DateTime paymentAttemptDate);
+    public void paymentAttemptFailed(UUID invoiceId, UUID paymentId, DateTime paymentAttemptDate);
 
-    public void paymentAttemptSuccessful(UUID invoiceId, DateTime paymentAttemptDate, BigDecimal paymentAmount);
+    public void paymentAttemptSuccessful(UUID invoiceId, BigDecimal amount, Currency currency,
+                                         UUID paymentId, DateTime paymentDate);
 }

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

diff --git a/beatrix/pom.xml b/beatrix/pom.xml
index 23586bb..07e2a57 100644
--- a/beatrix/pom.xml
+++ b/beatrix/pom.xml
@@ -13,7 +13,7 @@
     <parent>
         <groupId>com.ning.billing</groupId>
         <artifactId>killbill</artifactId>
-        <version>0.0.18-SNAPSHOT2</version>
+        <version>0.0.18-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>killbill-beatrix</artifactId>

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

diff --git a/catalog/pom.xml b/catalog/pom.xml
index 2700b48..ba9a714 100644
--- a/catalog/pom.xml
+++ b/catalog/pom.xml
@@ -13,7 +13,7 @@
     <parent>
         <groupId>com.ning.billing</groupId>
         <artifactId>killbill</artifactId>
-        <version>0.0.18-SNAPSHOT2</version>
+        <version>0.0.18-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>killbill-catalog</artifactId>
diff --git a/entitlement/pom.xml b/entitlement/pom.xml
index c3e15f7..93853ef 100644
--- a/entitlement/pom.xml
+++ b/entitlement/pom.xml
@@ -13,7 +13,7 @@
     <parent>
         <groupId>com.ning.billing</groupId>
         <artifactId>killbill</artifactId>
-        <version>0.0.18-SNAPSHOT2</version>
+        <version>0.0.18-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>killbill-entitlement</artifactId>

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

diff --git a/invoice/pom.xml b/invoice/pom.xml
index b8cb89a..8428ef0 100644
--- a/invoice/pom.xml
+++ b/invoice/pom.xml
@@ -13,7 +13,7 @@
     <parent>
         <groupId>com.ning.billing</groupId>
         <artifactId>killbill</artifactId>
-        <version>0.0.18-SNAPSHOT2</version>
+        <version>0.0.18-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>killbill-invoice</artifactId>
diff --git a/invoice/src/main/java/com/ning/billing/invoice/api/DefaultInvoiceService.java b/invoice/src/main/java/com/ning/billing/invoice/api/DefaultInvoiceService.java
index 33daa77..b7bc657 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/api/DefaultInvoiceService.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/api/DefaultInvoiceService.java
@@ -21,12 +21,12 @@ import com.ning.billing.lifecycle.LifecycleHandlerType;
 
 public class DefaultInvoiceService implements InvoiceService {
     private static final String INVOICE_SERVICE_NAME = "invoice-service";
-    //private final InvoiceUserApi userApi;
+    private final InvoiceUserApi userApi;
     private final InvoicePaymentApi paymentApi;
 
     @Inject
-    public DefaultInvoiceService(InvoicePaymentApi paymentApi) {
-        //this.userApi = userApi;
+    public DefaultInvoiceService(InvoiceUserApi userApi, InvoicePaymentApi paymentApi) {
+        this.userApi = userApi;
         this.paymentApi = paymentApi;
     }
 
@@ -37,7 +37,7 @@ public class DefaultInvoiceService implements InvoiceService {
 
     @Override
     public InvoiceUserApi getUserApi() {
-        return null;
+        return userApi;
     }
 
     @Override
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 572424c..22f4990 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
@@ -17,9 +17,12 @@
 package com.ning.billing.invoice.api.user;
 
 import com.google.inject.Inject;
+import com.ning.billing.catalog.api.Currency;
 import com.ning.billing.invoice.api.Invoice;
 import com.ning.billing.invoice.api.InvoiceUserApi;
+import com.ning.billing.invoice.dao.DefaultInvoiceDao;
 import com.ning.billing.invoice.dao.InvoiceDao;
+import com.ning.billing.util.eventbus.EventBus;
 import org.joda.time.DateTime;
 import org.skife.jdbi.v2.IDBI;
 
@@ -31,8 +34,8 @@ public class DefaultInvoiceUserApi implements InvoiceUserApi {
     private final InvoiceDao dao;
 
     @Inject
-    public DefaultInvoiceUserApi(IDBI dbi) {
-        dao = dbi.onDemand(InvoiceDao.class);
+    public DefaultInvoiceUserApi(InvoiceDao dao) {
+        this.dao = dao;
     }
 
     @Override
@@ -51,12 +54,12 @@ public class DefaultInvoiceUserApi implements InvoiceUserApi {
     }
 
     @Override
-    public void paymentAttemptFailed(UUID invoiceId, DateTime paymentAttemptDate) {
-        dao.notifyFailedPayment(invoiceId.toString(), paymentAttemptDate.toDate());
+    public void paymentAttemptFailed(UUID invoiceId, UUID paymentId, DateTime paymentAttemptDate) {
+        dao.notifyFailedPayment(invoiceId.toString(), paymentId.toString(), paymentAttemptDate.toDate());
     }
 
     @Override
-    public void paymentAttemptSuccessful(UUID invoiceId, DateTime paymentAttemptDate, BigDecimal paymentAmount) {
-        dao.notifySuccessfulPayment(invoiceId.toString(), paymentAttemptDate.toDate(), paymentAmount);
+    public void paymentAttemptSuccessful(UUID invoiceId, BigDecimal amount, Currency currency, UUID paymentId, DateTime paymentDate) {
+        dao.notifySuccessfulPayment(invoiceId.toString(), amount, currency.toString(), paymentId.toString(), paymentDate.toDate());
     }
 }
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 34b47c3..477be07 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,92 +16,33 @@
 
 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.InvoiceItem;
-import com.ning.billing.invoice.model.DefaultInvoice;
-import com.ning.billing.util.UuidMapper;
-import com.ning.billing.util.entity.EntityDao;
-import org.joda.time.DateTime;
-import org.skife.jdbi.v2.SQLStatement;
-import org.skife.jdbi.v2.StatementContext;
-import org.skife.jdbi.v2.sqlobject.*;
-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.*;
 import java.math.BigDecimal;
-import java.sql.ResultSet;
-import java.sql.SQLException;
-import java.sql.Timestamp;
-import java.util.ArrayList;
 import java.util.Date;
 import java.util.List;
 import java.util.UUID;
+import com.ning.billing.invoice.api.Invoice;
 
-@ExternalizedSqlViaStringTemplate3()
-@RegisterMapper({UuidMapper.class, InvoiceDao.InvoiceMapper.class})
-public interface InvoiceDao extends EntityDao<Invoice> {
-    @Override
-    @SqlUpdate
-    void save(@InvoiceBinder Invoice invoice);
-
-    @SqlQuery
-    List<Invoice> getInvoicesByAccount(@Bind("accountId") final String accountId);
+public interface InvoiceDao {
+    void save(Invoice invoice);
 
-    @SqlQuery
-    List<Invoice> getInvoicesBySubscription(@Bind("subscriptionId") final String subscriptionId);
+    Invoice getById(final String id);
 
-    @SqlQuery
-    List<UUID> getInvoicesForPayment(@Bind("targetDate") final Date targetDate,
-                                     @Bind("numberOfDays") final int numberOfDays);
+    List<Invoice> getInvoicesByAccount(final String accountId);
 
-    @SqlUpdate
-    void notifySuccessfulPayment(@Bind("id") final String invoiceId,
-                                 @Bind("paymentDate") final Date paymentDate,
-                                 @Bind("paymentAmount") final BigDecimal paymentAmount);
+    List<Invoice> getInvoicesBySubscription(final String subscriptionId);
 
-    @SqlUpdate
-    void notifyFailedPayment(@Bind("id") final String invoiceId,
-                             @Bind("paymentAttemptDate") final Date paymentAttemptDate);
+    List<UUID> getInvoicesForPayment(final Date targetDate,
+                                     final int numberOfDays);
 
-    @BindingAnnotation(InvoiceBinder.InvoiceBinderFactory.class)
-    @Retention(RetentionPolicy.RUNTIME)
-    @Target({ElementType.PARAMETER})
-    public @interface InvoiceBinder {
-        public static class InvoiceBinderFactory implements BinderFactory {
-            public Binder build(Annotation annotation) {
-                return new Binder<InvoiceBinder, Invoice>() {
-                    public void bind(SQLStatement q, InvoiceBinder bind, Invoice invoice) {
-                        q.bind("id", invoice.getId().toString());
-                        q.bind("accountId", invoice.getAccountId().toString());
-                        q.bind("invoiceDate", invoice.getInvoiceDate().toDate());
-                        q.bind("targetDate", invoice.getTargetDate().toDate());
-                        q.bind("amountPaid", invoice.getAmountPaid());
-                        q.bind("amountOutstanding", invoice.getAmountOutstanding());
-                        DateTime invoiceDate = invoice.getLastPaymentAttempt();
-                        q.bind("lastPaymentAttempt", invoiceDate == null ? null : invoiceDate.toDate());
-                        q.bind("currency", invoice.getCurrency().toString());
-                    }
-                };
-            }
-        }
-    }
+    void notifySuccessfulPayment(final String invoiceId,
+                                 final BigDecimal paymentAmount,
+                                 final String currency,
+                                 final String paymentId,
+                                 final Date paymentDate);
 
-    public static class InvoiceMapper implements ResultSetMapper<Invoice> {
-        @Override
-        public Invoice map(int index, ResultSet result, StatementContext context) throws SQLException {
-            UUID id = UUID.fromString(result.getString("id"));
-            UUID accountId = UUID.fromString(result.getString("account_id"));
-            DateTime invoiceDate = new DateTime(result.getTimestamp("invoice_date"));
-            DateTime targetDate = new DateTime(result.getTimestamp("target_date"));
-            BigDecimal amountPaid = result.getBigDecimal("amount_paid");
-            Timestamp lastPaymentAttemptTimeStamp = result.getTimestamp("last_payment_attempt");
-            DateTime lastPaymentAttempt = lastPaymentAttemptTimeStamp == null ? null : new DateTime(lastPaymentAttemptTimeStamp);
-            Currency currency = Currency.valueOf(result.getString("currency"));
+    void notifyFailedPayment(final String invoiceId,
+                             final String paymentId,
+                             final Date paymentAttemptDate);
 
-            return new DefaultInvoice(id, accountId, invoiceDate, targetDate, currency, lastPaymentAttempt, amountPaid, new ArrayList<InvoiceItem>());
-        }
-    }
+    void test();
 }
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
new file mode 100644
index 0000000..250b3f2
--- /dev/null
+++ b/invoice/src/main/java/com/ning/billing/invoice/dao/InvoiceSqlDao.java
@@ -0,0 +1,119 @@
+/*
+ * 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.Invoice;
+import com.ning.billing.invoice.api.InvoiceItem;
+import com.ning.billing.invoice.model.DefaultInvoice;
+import com.ning.billing.util.UuidMapper;
+import com.ning.billing.util.entity.EntityDao;
+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.SqlQuery;
+import org.skife.jdbi.v2.sqlobject.SqlUpdate;
+import org.skife.jdbi.v2.sqlobject.customizers.RegisterMapper;
+import org.skife.jdbi.v2.sqlobject.mixins.CloseMe;
+import org.skife.jdbi.v2.sqlobject.mixins.Transactional;
+import org.skife.jdbi.v2.sqlobject.mixins.Transmogrifier;
+import org.skife.jdbi.v2.sqlobject.stringtemplate.ExternalizedSqlViaStringTemplate3;
+import org.skife.jdbi.v2.tweak.ResultSetMapper;
+
+import java.lang.annotation.*;
+import java.math.BigDecimal;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Timestamp;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+import java.util.UUID;
+
+@ExternalizedSqlViaStringTemplate3()
+@RegisterMapper({UuidMapper.class, InvoiceSqlDao.InvoiceMapper.class})
+public interface InvoiceSqlDao extends EntityDao<Invoice>, Transactional<InvoiceSqlDao>, Transmogrifier, CloseMe {
+    @Override
+    @SqlUpdate
+    void save(@InvoiceBinder Invoice invoice);
+
+    @SqlQuery
+    List<Invoice> getInvoicesByAccount(@Bind("accountId") final String accountId);
+
+    @SqlQuery
+    List<Invoice> getInvoicesBySubscription(@Bind("subscriptionId") final String subscriptionId);
+
+    @SqlQuery
+    List<UUID> getInvoicesForPayment(@Bind("targetDate") final Date targetDate,
+                                     @Bind("numberOfDays") final int numberOfDays);
+
+    @SqlUpdate
+    void notifySuccessfulPayment(@Bind("invoiceId") final String invoiceId,
+                                 @Bind("paymentAmount") final BigDecimal paymentAmount,
+                                 @Bind("currency") final String currency,
+                                 @Bind("paymentId") final String paymentId,
+                                 @Bind("paymentDate") final Date paymentDate);
+
+    @SqlUpdate
+    void notifyFailedPayment(@Bind("invoiceId") final String invoiceId,
+                             @Bind("paymentId") final String paymentId,
+                             @Bind("paymentAttemptDate") final Date paymentAttemptDate);
+
+    @BindingAnnotation(InvoiceBinder.InvoiceBinderFactory.class)
+    @Retention(RetentionPolicy.RUNTIME)
+    @Target({ElementType.PARAMETER})
+    public @interface InvoiceBinder {
+        public static class InvoiceBinderFactory implements BinderFactory {
+            public Binder build(Annotation annotation) {
+                return new Binder<InvoiceBinder, Invoice>() {
+                    public void bind(SQLStatement q, InvoiceBinder bind, Invoice invoice) {
+                        q.bind("id", invoice.getId().toString());
+                        q.bind("accountId", invoice.getAccountId().toString());
+                        q.bind("invoiceDate", invoice.getInvoiceDate().toDate());
+                        q.bind("targetDate", invoice.getTargetDate().toDate());
+                        q.bind("amountPaid", invoice.getAmountPaid());
+                        q.bind("amountOutstanding", invoice.getAmountOutstanding());
+                        DateTime invoiceDate = invoice.getLastPaymentAttempt();
+                        q.bind("lastPaymentAttempt", invoiceDate == null ? null : invoiceDate.toDate());
+                        q.bind("currency", invoice.getCurrency().toString());
+                    }
+                };
+            }
+        }
+    }
+
+    public static class InvoiceMapper implements ResultSetMapper<Invoice> {
+        @Override
+        public Invoice map(int index, ResultSet result, StatementContext context) throws SQLException {
+            UUID id = UUID.fromString(result.getString("id"));
+            UUID accountId = UUID.fromString(result.getString("account_id"));
+            DateTime invoiceDate = new DateTime(result.getTimestamp("invoice_date"));
+            DateTime targetDate = new DateTime(result.getTimestamp("target_date"));
+            BigDecimal amountPaid = result.getBigDecimal("amount_paid");
+            Timestamp lastPaymentAttemptTimeStamp = result.getTimestamp("last_payment_attempt");
+            DateTime lastPaymentAttempt = lastPaymentAttemptTimeStamp == null ? null : new DateTime(lastPaymentAttemptTimeStamp);
+            Currency currency = Currency.valueOf(result.getString("currency"));
+
+            return new DefaultInvoice(id, accountId, invoiceDate, targetDate, currency, lastPaymentAttempt, amountPaid, new ArrayList<InvoiceItem>());
+        }
+    }
+}
+
diff --git a/invoice/src/main/java/com/ning/billing/invoice/glue/InvoiceModule.java b/invoice/src/main/java/com/ning/billing/invoice/glue/InvoiceModule.java
index 9b40875..54990c5 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/glue/InvoiceModule.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/glue/InvoiceModule.java
@@ -17,29 +17,30 @@
 package com.ning.billing.invoice.glue;
 
 import com.google.inject.AbstractModule;
+import com.ning.billing.invoice.api.InvoicePaymentApi;
+import com.ning.billing.invoice.api.invoice.DefaultInvoicePaymentApi;
 import com.ning.billing.invoice.api.user.DefaultInvoiceUserApi;
 import com.ning.billing.invoice.api.InvoiceUserApi;
+import com.ning.billing.invoice.dao.DefaultInvoiceDao;
 import com.ning.billing.invoice.dao.InvoiceDao;
-import com.ning.billing.invoice.dao.InvoiceDaoWrapper;
-import com.ning.billing.invoice.dao.InvoiceItemDao;
-import com.ning.billing.invoice.dao.InvoiceItemDaoWrapper;
 
 public class InvoiceModule extends AbstractModule {
     private void installInvoiceDao() {
-        bind(InvoiceDao.class).to(InvoiceDaoWrapper.class).asEagerSingleton();
-    }
-
-    private void installInvoiceItemDao() {
-        bind(InvoiceItemDao.class).to(InvoiceItemDaoWrapper.class).asEagerSingleton();
+        bind(InvoiceDao.class).to(DefaultInvoiceDao.class).asEagerSingleton();
     }
 
     protected void installInvoiceUserApi() {
         bind(InvoiceUserApi.class).to(DefaultInvoiceUserApi.class).asEagerSingleton();
     }
 
+    protected void installInvoicePaymentApi() {
+        bind(InvoicePaymentApi.class).to(DefaultInvoicePaymentApi.class).asEagerSingleton();
+    }
+
     @Override
     protected void configure() {
         installInvoiceDao();
-        installInvoiceItemDao();
+        installInvoiceUserApi();
+        installInvoicePaymentApi();
     }
 }
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
new file mode 100644
index 0000000..0f1f5d0
--- /dev/null
+++ b/invoice/src/main/resources/com/ning/billing/invoice/dao/InvoiceSqlDao.sql.stg
@@ -0,0 +1,62 @@
+group InvoiceDao;
+
+getInvoicesByAccount() ::= <<
+  SELECT i.id, i.account_id, i.invoice_date, i.target_date, i.currency, i.amount
+         SUM(ip.amount) AS amount_paid, MAX(ip.payment_date) AS last_payment_attempt
+  FROM i.invoices
+  LEFT JOIN invoice_payments ip ON ip.invoice_id = i.id
+  WHERE i.account_id = :accountId
+  GROUP BY i.id, i.account_id, i.invoice_date, i.target_date, i.currency
+  ORDER BY i.invoice_date ASC;
+>>
+
+getInvoicesBySubscription() ::= <<
+  SELECT i.id, i.account_id, i.invoice_date, i.target_date, i.currency,
+         SUM(ip.amount) AS amount_paid, MAX(ip.payment_date) AS last_payment_attempt
+  FROM invoices i
+  INNER JOIN invoice_items ii ON i.id = ii.invoice_id
+  LEFT JOIN invoice_payments ip ON ip.invoice_id = i.id
+  GROUP BY i.id, i.account_id, i.invoice_date, i.target_date, i.currency
+  WHERE ii.subscription_id = :subscriptionId;
+>>
+
+getInvoicesForPayment() ::= <<
+  SELECT i.id
+  FROM invoices i
+  INNER JOIN invoice_items ii ON i.id = ii.invoice_id
+  LEFT JOIN invoice_payments ip ON i.id = ip.invoice_id
+  WHERE DATEDIFF(:targetDate, MAX(ip.payment_date)) >= :numberOfDays OR (MAX(ip.payment_date) IS NULL)
+  GROUP BY i.id, i.account_id, i.invoice_date, i.target_date, i.currency
+  HAVING SUM(ii.amount) > SUM(ip.amount);
+>>
+
+getById() ::= <<
+  SELECT i.id, i.account_id, i.invoice_date, i.target_date, SUM(ii.amount), i.currency
+  FROM invoices i
+  INNER JOIN invoice_items ii ON i.id = ii.invoice_id
+  GROUP BY i.id, i.account_id, i.invoice_date, i.target_date, i.currency
+  WHERE i.id = :id;
+>>
+
+save() ::= <<
+  INSERT INTO invoices(id, account_id, invoice_date, target_date, currency)
+  VALUES (:id, :accountId, :invoiceDate, :targetDate, :currency)
+  ON DUPLICATE KEY UPDATE
+    invoice_date = :invoiceDate, target_date = :targetDate, currency = :currency;
+>>
+
+notifySuccessfulPayment() ::= <<
+  INSERT INTO invoice_payments(invoice_id, payment_id, payment_date, amount, currency)
+  VALUES(:invoiceId, :paymentId, :paymentDate, :amount, :currency);
+>>
+
+notifyFailedPayment() ::= <<
+  INSERT INTO invoice_payments(invoice_id, payment_id, payment_date)
+  VALUES(:invoiceId, :paymentId, :paymentDate);
+>>
+
+test() ::= <<
+  SELECT 1
+  FROM invoices;
+>>
+;
\ No newline at end of file
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 3c25d8e..f6f2c41 100644
--- a/invoice/src/main/resources/com/ning/billing/invoice/ddl.sql
+++ b/invoice/src/main/resources/com/ning/billing/invoice/ddl.sql
@@ -21,8 +21,6 @@ CREATE TABLE invoices (
   invoice_date datetime NOT NULL,
   target_date datetime NOT NULL,
   currency char(3) NOT NULL,
-  amount_paid numeric(10,4) NOT NULL DEFAULT 0,
-  last_payment_attempt datetime DEFAULT NULL,
   PRIMARY KEY(id)
 ) ENGINE=innodb;
 
@@ -30,30 +28,11 @@ CREATE INDEX invoices_account_id ON invoices(account_id ASC);
 
 DROP TABLE IF EXISTS invoice_payments;
 CREATE TABLE invoice_payments (
-  id char(36) NOT NULL,
   invoice_id char(36) NOT NULL,
   payment_id char(36) NOT NULL,
   payment_date datetime NOT NULL,
-  amount numeric(10,4) NOT NULL,
-  currency char(3) NOT NULL,
-  PRIMARY KEY(id)
+  amount numeric(10,4),
+  currency char(3),
+  PRIMARY KEY(invoice_id, payment_id)
 ) ENGINE=innodb;
-CREATE UNIQUE INDEX invoice_payments_unique ON invoice_payments(invoice_id, payment_id);
-
-CREATE VIEW amount_remaining AS
-SELECT invoice_items.id,
-       SUM(invoice_items.amount) AS amount_owed
-FROM invoice_items
-GROUP BY invoice_items.id;
-
-CREATE VIEW invoices_for_payment AS
-SELECT i.id,
-       DATEDIFF(NOW(), MAX(i.last_payment_attempt)) AS days_due
-FROM invoices i
-LEFT JOIN amount_remaining ar ON i.id = ar.id
-WHERE (i.amount_paid < ar.amount_owed)
-      AND (i.last_payment_attempt IS NOT NULL)
-GROUP BY i.id;
-
-
-
+CREATE UNIQUE INDEX invoice_payments_unique ON invoice_payments(invoice_id, payment_id);
\ No newline at end of file
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 5d31a5f..b9bddb8 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
@@ -30,22 +30,22 @@ import static org.testng.Assert.fail;
 
 public abstract class InvoiceDaoTestBase {
     protected InvoiceDao invoiceDao;
-    protected InvoiceItemDao invoiceItemDao;
+    protected InvoiceItemSqlDao invoiceItemDao;
 
     @BeforeClass()
     protected void setup() throws IOException {
         // Health check test to make sure MySQL is setup properly
         try {
             InvoiceModuleMock module = new InvoiceModuleMock();
-            final String ddl = IOUtils.toString(InvoiceDaoWrapper.class.getResourceAsStream("/com/ning/billing/invoice/ddl.sql"));
+            final String ddl = IOUtils.toString(DefaultInvoiceDao.class.getResourceAsStream("/com/ning/billing/invoice/ddl.sql"));
             module.createDb(ddl);
 
             final Injector injector = Guice.createInjector(Stage.DEVELOPMENT, module);
 
-            invoiceDao = injector.getInstance(InvoiceDaoWrapper.class);
+            invoiceDao = injector.getInstance(InvoiceDao.class);
             invoiceDao.test();
 
-            invoiceItemDao = injector.getInstance(InvoiceItemDao.class);
+            invoiceItemDao = injector.getInstance(InvoiceItemSqlDao.class);
             invoiceItemDao.test();
 
             EventBusService busService = injector.getInstance(EventBusService.class);
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 675bdeb..6f5f708 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
@@ -16,28 +16,22 @@
 
 package com.ning.billing.invoice.dao;
 
-import com.google.inject.Guice;
-import com.google.inject.Injector;
-import com.google.inject.Stage;
+import java.math.BigDecimal;
+import java.util.List;
+import java.util.UUID;
+import org.joda.time.DateTime;
+import org.testng.annotations.Test;
 import com.ning.billing.catalog.api.Currency;
 import com.ning.billing.invoice.api.Invoice;
-import com.ning.billing.invoice.glue.InvoiceModuleMock;
+import com.ning.billing.invoice.api.InvoiceItem;
 import com.ning.billing.invoice.model.DefaultInvoice;
 import com.ning.billing.invoice.model.DefaultInvoiceItem;
 import com.ning.billing.util.clock.DefaultClock;
-import com.ning.billing.util.eventbus.DefaultEventBusService;
-import com.ning.billing.util.eventbus.EventBusService;
-import org.apache.commons.io.IOUtils;
-import org.joda.time.DateTime;
-import org.testng.annotations.BeforeClass;
-import org.testng.annotations.Test;
-
-import java.io.IOException;
-import java.math.BigDecimal;
-import java.util.List;
-import java.util.UUID;
 
-import static org.testng.Assert.*;
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNotNull;
+import static org.testng.Assert.assertNull;
+import static org.testng.Assert.assertTrue;
 
 @Test(groups = {"invoicing", "invoicing-invoiceDao"})
 public class InvoiceDaoTests extends InvoiceDaoTestBase {
@@ -63,6 +57,37 @@ public class InvoiceDaoTests extends InvoiceDaoTestBase {
     }
 
     @Test
+    public void testInvoicePayment() {
+        UUID accountId = UUID.randomUUID();
+        Invoice invoice = new DefaultInvoice(accountId, new DefaultClock().getUTCNow(), Currency.USD);
+        UUID invoiceId = invoice.getId();
+        UUID subscriptionId = UUID.randomUUID();
+        DateTime startDate = new DateTime(2010, 1, 1, 0, 0, 0, 0);
+        DateTime endDate = new DateTime(2010, 4, 1, 0, 0, 0, 0);
+        InvoiceItem invoiceItem = new DefaultInvoiceItem(invoiceId, subscriptionId, startDate, endDate, "test", new BigDecimal("21.00"), new BigDecimal("7.00"), Currency.USD);
+        invoice.add(invoiceItem);
+        invoiceDao.save(invoice);
+
+        Invoice savedInvoice = invoiceDao.getById(invoiceId.toString());
+        assertNotNull(savedInvoice);
+        assertEquals(savedInvoice.getTotalAmount(), new BigDecimal("21.00"));
+        assertEquals(savedInvoice.getAmountOutstanding(), new BigDecimal("21.00"));
+        assertEquals(savedInvoice.getAmountPaid(), BigDecimal.ZERO);
+        assertEquals(savedInvoice.getItems().size(), 1);
+
+        BigDecimal paymentAmount = new BigDecimal("11.00");
+        String paymentId = UUID.randomUUID().toString();
+        invoiceDao.notifySuccessfulPayment(invoiceId.toString(), paymentAmount, Currency.USD.toString(), paymentId, new DefaultClock().getUTCNow().plusDays(12).toDate());
+
+        Invoice retrievedInvoice = invoiceDao.getById(invoiceId.toString());
+        assertNotNull(retrievedInvoice);
+        assertEquals(retrievedInvoice.getTotalAmount(), new BigDecimal("21.00"));
+        assertEquals(retrievedInvoice.getAmountOutstanding(), new BigDecimal("10.00"));
+        assertEquals(retrievedInvoice.getAmountPaid(), new BigDecimal("11.00"));
+        assertEquals(retrievedInvoice.getItems().size(), 1);
+    }
+
+    @Test
     public void testRetrievalForNonExistentInvoiceId() {
         Invoice invoice = invoiceDao.getById(UUID.randomUUID().toString());
         assertNull(invoice);
@@ -74,11 +99,12 @@ public class InvoiceDaoTests extends InvoiceDaoTestBase {
         DateTime targetDate = new DateTime(2011, 10, 6, 0, 0, 0, 0);
         Invoice invoice = new DefaultInvoice(accountId, targetDate, Currency.USD);
 
+        String paymentId = UUID.randomUUID().toString();
         DateTime paymentAttemptDate = new DateTime(2011, 6, 24, 12, 14, 36, 0);
         BigDecimal paymentAmount = new BigDecimal("14.0");
 
         invoiceDao.save(invoice);
-        invoiceDao.notifySuccessfulPayment(invoice.getId().toString(), paymentAttemptDate.toDate(), paymentAmount);
+        invoiceDao.notifySuccessfulPayment(invoice.getId().toString(), paymentAmount, Currency.USD.toString(), paymentId, paymentAttemptDate.toDate());
 
         invoice = invoiceDao.getById(invoice.getId().toString());
         assertEquals(invoice.getAmountPaid().compareTo(paymentAmount), 0);
@@ -94,7 +120,7 @@ public class InvoiceDaoTests extends InvoiceDaoTestBase {
         DateTime paymentAttemptDate = new DateTime(2011, 6, 24, 12, 14, 36, 0);
 
         invoiceDao.save(invoice);
-        invoiceDao.notifyFailedPayment(invoice.getId().toString(), paymentAttemptDate.toDate());
+        invoiceDao.notifyFailedPayment(invoice.getId().toString(), UUID.randomUUID().toString(), paymentAttemptDate.toDate());
 
         invoice = invoiceDao.getById(invoice.getId().toString());
         assertTrue(invoice.getLastPaymentAttempt().equals(paymentAttemptDate));
@@ -146,7 +172,7 @@ public class InvoiceDaoTests extends InvoiceDaoTestBase {
         assertEquals(invoices.size(), existingInvoiceCount + 1);
 
         // attempt a payment; ensure that the number of invoices for payment has decreased by 1 (no retries for eight days)
-        invoiceDao.notifyFailedPayment(invoice.getId().toString(), notionalDate.toDate());
+        invoiceDao.notifyFailedPayment(invoice.getId().toString(), UUID.randomUUID().toString(), notionalDate.toDate());
         invoices = invoiceDao.getInvoicesForPayment(notionalDate.toDate(), NUMBER_OF_DAY_BETWEEN_RETRIES);
         assertEquals(invoices.size(), existingInvoiceCount);
 
@@ -156,7 +182,7 @@ public class InvoiceDaoTests extends InvoiceDaoTestBase {
         assertEquals(invoices.size(), existingInvoiceCount + 1);
 
         // post successful partial payment; ensure that number of invoices for payment has decreased by 1
-        invoiceDao.notifySuccessfulPayment(invoiceId.toString(), notionalDate.toDate(), new BigDecimal("22.0000"));
+        invoiceDao.notifySuccessfulPayment(invoiceId.toString(), new BigDecimal("22.0000"), Currency.USD.toString(), UUID.randomUUID().toString(), notionalDate.toDate());
         invoices = invoiceDao.getInvoicesForPayment(notionalDate.toDate(), NUMBER_OF_DAY_BETWEEN_RETRIES);
         assertEquals(invoices.size(), existingInvoiceCount);
 
@@ -170,7 +196,7 @@ public class InvoiceDaoTests extends InvoiceDaoTestBase {
         assertEquals(invoices.size(), existingInvoiceCount + 1);
 
         // post completed payment; ensure that the number of invoices for payment has decreased by 1
-        invoiceDao.notifySuccessfulPayment(invoiceId.toString(), notionalDate.toDate(), new BigDecimal("5.0000"));
+        invoiceDao.notifySuccessfulPayment(invoiceId.toString(), new BigDecimal("5.0000"), Currency.USD.toString(), UUID.randomUUID().toString(), notionalDate.toDate());
         invoices = invoiceDao.getInvoicesForPayment(notionalDate.toDate(), NUMBER_OF_DAY_BETWEEN_RETRIES);
         assertEquals(invoices.size(), existingInvoiceCount);
 

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

diff --git a/payment/pom.xml b/payment/pom.xml
index 66e36f7..ea4716f 100644
--- a/payment/pom.xml
+++ b/payment/pom.xml
@@ -13,7 +13,7 @@
     <parent>
         <groupId>com.ning.billing</groupId>
         <artifactId>killbill</artifactId>
-        <version>0.0.18-SNAPSHOT2</version>
+        <version>0.0.18-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>killbill-payment</artifactId>

pom.xml 2(+1 -1)

diff --git a/pom.xml b/pom.xml
index 8ddc84c..86eeec4 100644
--- a/pom.xml
+++ b/pom.xml
@@ -17,7 +17,7 @@
     <groupId>com.ning.billing</groupId>
     <artifactId>killbill</artifactId>
     <packaging>pom</packaging>
-    <version>0.0.18-SNAPSHOT2</version>
+    <version>0.0.18-SNAPSHOT</version>
     <name>killbill</name>
     <description>Library for managing recurring subscriptions and the associated billing</description>
     <url>http://github.com/ning/killbill</url>

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

diff --git a/util/pom.xml b/util/pom.xml
index fda8ebf..ad4848e 100644
--- a/util/pom.xml
+++ b/util/pom.xml
@@ -13,7 +13,7 @@
     <parent>
         <groupId>com.ning.billing</groupId>
         <artifactId>killbill</artifactId>
-        <version>0.0.18-SNAPSHOT2</version>
+        <version>0.0.18-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>killbill-util</artifactId>
diff --git a/util/src/main/java/com/ning/billing/util/entity/EntityDao.java b/util/src/main/java/com/ning/billing/util/entity/EntityDao.java
index 4207d5f..1ee4fc1 100644
--- a/util/src/main/java/com/ning/billing/util/entity/EntityDao.java
+++ b/util/src/main/java/com/ning/billing/util/entity/EntityDao.java
@@ -18,6 +18,7 @@ package com.ning.billing.util.entity;
 
 import org.skife.jdbi.v2.sqlobject.Bind;
 import org.skife.jdbi.v2.sqlobject.BindBean;
+import org.skife.jdbi.v2.sqlobject.SqlBatch;
 import org.skife.jdbi.v2.sqlobject.SqlQuery;
 import org.skife.jdbi.v2.sqlobject.SqlUpdate;