killbill-aplcache

analytics: add plumbing to listen to payment events Record

7/5/2012 4:51:20 PM

Changes

Details

diff --git a/analytics/src/main/java/com/ning/billing/analytics/AnalyticsListener.java b/analytics/src/main/java/com/ning/billing/analytics/AnalyticsListener.java
index af8c7c6..6337422 100644
--- a/analytics/src/main/java/com/ning/billing/analytics/AnalyticsListener.java
+++ b/analytics/src/main/java/com/ning/billing/analytics/AnalyticsListener.java
@@ -44,6 +44,7 @@ public class AnalyticsListener {
     private final BusinessAccountRecorder bacRecorder;
     private final BusinessInvoiceRecorder invoiceRecorder;
     private final BusinessOverdueStatusRecorder bosRecorder;
+    private final BusinessInvoicePaymentRecorder bipRecorder;
     private final BusinessTagRecorder tagRecorder;
 
     @Inject
@@ -51,11 +52,13 @@ public class AnalyticsListener {
                              final BusinessAccountRecorder bacRecorder,
                              final BusinessInvoiceRecorder invoiceRecorder,
                              final BusinessOverdueStatusRecorder bosRecorder,
+                             final BusinessInvoicePaymentRecorder bipRecorder,
                              final BusinessTagRecorder tagRecorder) {
         this.bstRecorder = bstRecorder;
         this.bacRecorder = bacRecorder;
         this.invoiceRecorder = invoiceRecorder;
         this.bosRecorder = bosRecorder;
+        this.bipRecorder = bipRecorder;
         this.tagRecorder = tagRecorder;
     }
 
@@ -104,12 +107,18 @@ public class AnalyticsListener {
 
     @Subscribe
     public void handlePaymentInfo(final PaymentInfoEvent paymentInfo) {
-        bacRecorder.accountUpdated(paymentInfo);
+        bipRecorder.invoicePaymentPosted(paymentInfo.getAccountId(),
+                                         paymentInfo.getPaymentId(),
+                                         paymentInfo.getExtPaymentRefId(),
+                                         paymentInfo.getStatus().toString());
     }
 
     @Subscribe
     public void handlePaymentError(final PaymentErrorEvent paymentError) {
-        // TODO - we can't tie the error back to an account yet
+        bipRecorder.invoicePaymentPosted(paymentError.getAccountId(),
+                                         paymentError.getPaymentId(),
+                                         null,
+                                         paymentError.getMessage());
     }
 
     @Subscribe
diff --git a/analytics/src/main/java/com/ning/billing/analytics/api/user/DefaultAnalyticsUserApi.java b/analytics/src/main/java/com/ning/billing/analytics/api/user/DefaultAnalyticsUserApi.java
index 102fb95..4d90894 100644
--- a/analytics/src/main/java/com/ning/billing/analytics/api/user/DefaultAnalyticsUserApi.java
+++ b/analytics/src/main/java/com/ning/billing/analytics/api/user/DefaultAnalyticsUserApi.java
@@ -25,6 +25,7 @@ import com.ning.billing.analytics.model.BusinessAccount;
 import com.ning.billing.analytics.model.BusinessAccountTag;
 import com.ning.billing.analytics.model.BusinessInvoice;
 import com.ning.billing.analytics.model.BusinessInvoiceItem;
+import com.ning.billing.analytics.model.BusinessInvoicePayment;
 import com.ning.billing.analytics.model.BusinessOverdueStatus;
 import com.ning.billing.analytics.model.BusinessSubscriptionTransition;
 
@@ -60,4 +61,8 @@ public class DefaultAnalyticsUserApi {
     public List<BusinessInvoiceItem> getInvoiceItemsForInvoice(final UUID invoiceId) {
         return analyticsDao.getInvoiceItemsForInvoice(invoiceId.toString());
     }
+
+    public List<BusinessInvoicePayment> getInvoicePaymentsForAccount(final String accountKey) {
+        return analyticsDao.getInvoicePaymentsForAccountByKey(accountKey);
+    }
 }
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 0919746..7ecd1b3 100644
--- a/analytics/src/main/java/com/ning/billing/analytics/BusinessAccountRecorder.java
+++ b/analytics/src/main/java/com/ning/billing/analytics/BusinessAccountRecorder.java
@@ -36,7 +36,6 @@ import com.ning.billing.invoice.api.InvoiceUserApi;
 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.payment.api.PaymentInfoEvent;
 
 public class BusinessAccountRecorder {
     private static final Logger log = LoggerFactory.getLogger(BusinessAccountRecorder.class);
@@ -70,41 +69,33 @@ public class BusinessAccountRecorder {
     }
 
     /**
-     * Notification handler for Payment creations
-     *
-     * @param paymentInfo payment object (from the payment plugin)
-     */
-    public void accountUpdated(final PaymentInfoEvent paymentInfo) {
-        try {
-            final Account account = accountApi.getAccountById(paymentInfo.getAccountId());
-            accountUpdated(account.getId());
-        } catch (AccountApiException e) {
-            log.warn("Error encountered creating BusinessAccount", e);
-        }
-    }
-
-    /**
      * Notification handler for Invoice creations
      *
      * @param accountId account id associated with the created invoice
      */
     public void accountUpdated(final UUID accountId) {
+        final Account account;
         try {
-            final Account account = accountApi.getAccountById(accountId);
-
-            BusinessAccount bac = sqlDao.getAccount(accountId.toString());
-            if (bac == null) {
-                bac = new BusinessAccount(accountId);
-                updateBusinessAccountFromAccount(account, bac);
-                log.info("ACCOUNT CREATION " + bac);
-                sqlDao.createAccount(bac);
-            } else {
-                updateBusinessAccountFromAccount(account, bac);
-                log.info("ACCOUNT UPDATE " + bac);
-                sqlDao.saveAccount(bac);
-            }
+            account = accountApi.getAccountById(accountId);
         } catch (AccountApiException e) {
             log.warn("Error encountered creating BusinessAccount", e);
+            return;
+        }
+
+        updateAccountInTransaction(account, sqlDao);
+    }
+
+    public void updateAccountInTransaction(final Account account, final BusinessAccountSqlDao transactional) {
+        BusinessAccount bac = transactional.getAccount(account.getId().toString());
+        if (bac == null) {
+            bac = new BusinessAccount(account.getId());
+            updateBusinessAccountFromAccount(account, bac);
+            log.info("ACCOUNT CREATION " + bac);
+            transactional.createAccount(bac);
+        } else {
+            updateBusinessAccountFromAccount(account, bac);
+            log.info("ACCOUNT UPDATE " + bac);
+            transactional.saveAccount(bac);
         }
     }
 
@@ -142,7 +133,7 @@ public class BusinessAccountRecorder {
                         if (lastPaymentDate == null || cur.getEffectiveDate().isAfter(lastPaymentDate)) {
                             lastPaymentDate = cur.getEffectiveDate();
                             lastPaymentStatus = cur.getPaymentStatus().toString();
-                            // STEPH talk to Pierre
+                            // TODO STEPH talk to Pierre
                             paymentMethod = null;
                             creditCardType = null;
                             billingAddressCountry = null;
diff --git a/analytics/src/main/java/com/ning/billing/analytics/BusinessInvoicePaymentRecorder.java b/analytics/src/main/java/com/ning/billing/analytics/BusinessInvoicePaymentRecorder.java
new file mode 100644
index 0000000..4904b80
--- /dev/null
+++ b/analytics/src/main/java/com/ning/billing/analytics/BusinessInvoicePaymentRecorder.java
@@ -0,0 +1,139 @@
+/*
+ * Copyright 2010-2012 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.analytics;
+
+import javax.annotation.Nullable;
+import javax.inject.Inject;
+import java.util.UUID;
+
+import org.skife.jdbi.v2.Transaction;
+import org.skife.jdbi.v2.TransactionStatus;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+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.analytics.dao.BusinessAccountSqlDao;
+import com.ning.billing.analytics.dao.BusinessInvoicePaymentSqlDao;
+import com.ning.billing.analytics.dao.BusinessInvoiceSqlDao;
+import com.ning.billing.analytics.model.BusinessInvoicePayment;
+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.payment.api.PaymentMethod;
+import com.ning.billing.payment.api.PaymentMethodPlugin;
+import com.ning.billing.util.clock.Clock;
+
+public class BusinessInvoicePaymentRecorder {
+    private static final Logger log = LoggerFactory.getLogger(BusinessInvoicePaymentRecorder.class);
+
+    private final BusinessInvoicePaymentSqlDao invoicePaymentSqlDao;
+    private final AccountUserApi accountApi;
+    private final PaymentApi paymentApi;
+    private final Clock clock;
+    private final BusinessInvoiceRecorder invoiceRecorder;
+    private final BusinessAccountRecorder accountRecorder;
+
+    @Inject
+    public BusinessInvoicePaymentRecorder(final BusinessInvoicePaymentSqlDao invoicePaymentSqlDao, final AccountUserApi accountApi,
+                                          final PaymentApi paymentApi, final Clock clock, final BusinessInvoiceRecorder invoiceRecorder,
+                                          final BusinessAccountRecorder accountRecorder) {
+        this.invoicePaymentSqlDao = invoicePaymentSqlDao;
+        this.accountApi = accountApi;
+        this.paymentApi = paymentApi;
+        this.clock = clock;
+        this.invoiceRecorder = invoiceRecorder;
+        this.accountRecorder = accountRecorder;
+    }
+
+    public void invoicePaymentPosted(final UUID accountId, final UUID paymentId, @Nullable final String extPaymentRefId, final String message) {
+        final Account account;
+        try {
+            account = accountApi.getAccountById(accountId);
+        } catch (AccountApiException e) {
+            log.warn("Ignoring payment {}: account {} does not exist", paymentId, accountId);
+            return;
+        }
+
+        final Payment payment;
+        try {
+            payment = paymentApi.getPayment(paymentId);
+        } catch (PaymentApiException e) {
+            log.warn("Ignoring payment {}: payment does not exist", paymentId);
+            return;
+        }
+
+        final PaymentMethod paymentMethod;
+        try {
+            paymentMethod = paymentApi.getPaymentMethod(account, payment.getPaymentMethodId(), true);
+        } catch (PaymentApiException e) {
+            log.warn("Ignoring payment {}: payment method {} does not exist", paymentId, payment.getPaymentMethodId());
+            return;
+        }
+
+        createPayment(account, payment, paymentMethod, extPaymentRefId, message);
+    }
+
+    private void createPayment(final Account account, final Payment payment, final PaymentMethod paymentMethod,
+                               final String extPaymentRefId, final String message) {
+        final PaymentMethodPlugin pluginDetail = paymentMethod.getPluginDetail();
+        // TODO - make it generic
+        final String cardCountry = pluginDetail != null ? pluginDetail.getValueString("country") : null;
+        final String cardType = pluginDetail != null ? pluginDetail.getValueString("cardType") : null;
+        // TODO support CreditCard, DebitCard, WireTransfer, BankTransfer, Check, ACH, Cash, Paypal
+        final String paymentMethodString = cardType != null ? "CreditCard" : "Other";
+
+        invoicePaymentSqlDao.inTransaction(new Transaction<Void, BusinessInvoicePaymentSqlDao>() {
+            @Override
+            public Void inTransaction(final BusinessInvoicePaymentSqlDao transactional, final TransactionStatus status) throws Exception {
+                // Create the bip record
+                final BusinessInvoicePayment invoicePayment = new BusinessInvoicePayment(
+                        account.getExternalKey(),
+                        payment.getAmount(),
+                        extPaymentRefId,
+                        cardCountry,
+                        cardType,
+                        clock.getUTCNow(),
+                        payment.getCurrency(),
+                        payment.getEffectiveDate(),
+                        payment.getInvoiceId(),
+                        message,
+                        payment.getId(),
+                        paymentMethodString,
+                        "Electronic",
+                        paymentMethod.getPluginName(),
+                        payment.getPaymentStatus().toString(),
+                        payment.getAmount(),
+                        clock.getUTCNow()
+                );
+                transactional.createInvoicePayment(invoicePayment);
+
+                // Update bin to get the latest invoice(s) balance(s)
+                final BusinessInvoiceSqlDao invoiceSqlDao = transactional.become(BusinessInvoiceSqlDao.class);
+                invoiceRecorder.rebuildInvoicesForAccountInTransaction(account.getId(), invoiceSqlDao);
+
+                // Update bac to get the latest account balance, total invoice balance, etc.
+                final BusinessAccountSqlDao accountSqlDao = transactional.become(BusinessAccountSqlDao.class);
+                accountRecorder.updateAccountInTransaction(account, accountSqlDao);
+
+                log.info("Added payment {}", invoicePayment);
+                return null;
+            }
+        });
+    }
+}
diff --git a/analytics/src/main/java/com/ning/billing/analytics/BusinessInvoiceRecorder.java b/analytics/src/main/java/com/ning/billing/analytics/BusinessInvoiceRecorder.java
index b3c6996..30baa4a 100644
--- a/analytics/src/main/java/com/ning/billing/analytics/BusinessInvoiceRecorder.java
+++ b/analytics/src/main/java/com/ning/billing/analytics/BusinessInvoiceRecorder.java
@@ -69,6 +69,16 @@ public class BusinessInvoiceRecorder {
     }
 
     public void rebuildInvoicesForAccount(final UUID accountId) {
+        sqlDao.inTransaction(new Transaction<Void, BusinessInvoiceSqlDao>() {
+            @Override
+            public Void inTransaction(final BusinessInvoiceSqlDao transactional, final TransactionStatus status) throws Exception {
+                rebuildInvoicesForAccountInTransaction(accountId, transactional);
+                return null;
+            }
+        });
+    }
+
+    public void rebuildInvoicesForAccountInTransaction(final UUID accountId, final BusinessInvoiceSqlDao transactional) {
         // Lookup the associated account
         final String accountKey;
         try {
@@ -79,20 +89,14 @@ public class BusinessInvoiceRecorder {
             return;
         }
 
-        sqlDao.inTransaction(new Transaction<Void, BusinessInvoiceSqlDao>() {
-            @Override
-            public Void inTransaction(final BusinessInvoiceSqlDao transactional, final TransactionStatus status) throws Exception {
-                log.info("Started rebuilding transitions for account id {}", accountId);
-                deleteInvoicesAndInvoiceItemsForAccountInTransaction(transactional, accountId);
+        log.info("Started rebuilding transitions for account id {}", accountId);
+        deleteInvoicesAndInvoiceItemsForAccountInTransaction(transactional, accountId);
 
-                for (final Invoice invoice : invoiceApi.getInvoicesByAccount(accountId)) {
-                    createInvoiceInTransaction(transactional, accountKey, invoice);
-                }
+        for (final Invoice invoice : invoiceApi.getInvoicesByAccount(accountId)) {
+            createInvoiceInTransaction(transactional, accountKey, invoice);
+        }
 
-                log.info("Finished rebuilding transitions for account id {}", accountId);
-                return null;
-            }
-        });
+        log.info("Finished rebuilding transitions for account id {}", accountId);
     }
 
     private void deleteInvoicesAndInvoiceItemsForAccountInTransaction(final BusinessInvoiceSqlDao transactional, final UUID accountId) {
diff --git a/analytics/src/main/java/com/ning/billing/analytics/dao/AnalyticsDao.java b/analytics/src/main/java/com/ning/billing/analytics/dao/AnalyticsDao.java
index 97d72f2..0e349fc 100644
--- a/analytics/src/main/java/com/ning/billing/analytics/dao/AnalyticsDao.java
+++ b/analytics/src/main/java/com/ning/billing/analytics/dao/AnalyticsDao.java
@@ -22,6 +22,7 @@ import com.ning.billing.analytics.model.BusinessAccount;
 import com.ning.billing.analytics.model.BusinessAccountTag;
 import com.ning.billing.analytics.model.BusinessInvoice;
 import com.ning.billing.analytics.model.BusinessInvoiceItem;
+import com.ning.billing.analytics.model.BusinessInvoicePayment;
 import com.ning.billing.analytics.model.BusinessOverdueStatus;
 import com.ning.billing.analytics.model.BusinessSubscriptionTransition;
 
@@ -37,4 +38,6 @@ public interface AnalyticsDao {
     List<BusinessInvoiceItem> getInvoiceItemsForInvoice(final String invoiceId);
 
     List<BusinessOverdueStatus> getOverdueStatusesForBundleByKey(final String externalKey);
+
+    List<BusinessInvoicePayment> getInvoicePaymentsForAccountByKey(final String accountKey);
 }
diff --git a/analytics/src/main/java/com/ning/billing/analytics/dao/BusinessInvoicePaymentBinder.java b/analytics/src/main/java/com/ning/billing/analytics/dao/BusinessInvoicePaymentBinder.java
index 8c01e8f..e00d675 100644
--- a/analytics/src/main/java/com/ning/billing/analytics/dao/BusinessInvoicePaymentBinder.java
+++ b/analytics/src/main/java/com/ning/billing/analytics/dao/BusinessInvoicePaymentBinder.java
@@ -56,7 +56,7 @@ public @interface BusinessInvoicePaymentBinder {
                         q.bind("updated_date", dateTimeNow.getMillis());
                     }
 
-                    q.bind("attempt_id", invoicePayment.getAttemptId().toString());
+                    q.bind("ext_payment_ref_id", invoicePayment.getExtPaymentRefId());
                     q.bind("account_key", invoicePayment.getAccountKey());
                     q.bind("invoice_id", invoicePayment.getInvoiceId().toString());
 
diff --git a/analytics/src/main/java/com/ning/billing/analytics/dao/BusinessInvoicePaymentMapper.java b/analytics/src/main/java/com/ning/billing/analytics/dao/BusinessInvoicePaymentMapper.java
index af80d53..b658006 100644
--- a/analytics/src/main/java/com/ning/billing/analytics/dao/BusinessInvoicePaymentMapper.java
+++ b/analytics/src/main/java/com/ning/billing/analytics/dao/BusinessInvoicePaymentMapper.java
@@ -35,7 +35,7 @@ public class BusinessInvoicePaymentMapper implements ResultSetMapper<BusinessInv
         final UUID paymentId = UUID.fromString(r.getString(1));
         final DateTime createdDate = new DateTime(r.getLong(2), DateTimeZone.UTC);
         final DateTime updatedDate = new DateTime(r.getLong(3), DateTimeZone.UTC);
-        final UUID attemptId = UUID.fromString(r.getString(4));
+        final String extPaymentRefId = r.getString(4);
         final String accountKey = r.getString(5);
         final UUID invoiceId = UUID.fromString(r.getString(6));
         final DateTime effectiveDate = new DateTime(r.getLong(7), DateTimeZone.UTC);
@@ -50,7 +50,7 @@ public class BusinessInvoicePaymentMapper implements ResultSetMapper<BusinessInv
         final String cardType = r.getString(16);
         final String cardCountry = r.getString(17);
 
-        return new BusinessInvoicePayment(accountKey, amount, attemptId, cardCountry, cardType, createdDate, currency,
+        return new BusinessInvoicePayment(accountKey, amount, extPaymentRefId, cardCountry, cardType, createdDate, currency,
                                           effectiveDate, invoiceId, paymentError, paymentId, paymentMethod, paymentType,
                                           pluginName, processingStatus, requestedAmount, updatedDate);
     }
diff --git a/analytics/src/main/java/com/ning/billing/analytics/dao/BusinessInvoicePaymentSqlDao.java b/analytics/src/main/java/com/ning/billing/analytics/dao/BusinessInvoicePaymentSqlDao.java
index 9c651d9..d21613a 100644
--- a/analytics/src/main/java/com/ning/billing/analytics/dao/BusinessInvoicePaymentSqlDao.java
+++ b/analytics/src/main/java/com/ning/billing/analytics/dao/BusinessInvoicePaymentSqlDao.java
@@ -22,18 +22,17 @@ import org.skife.jdbi.v2.sqlobject.Bind;
 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.Transactional;
+import org.skife.jdbi.v2.sqlobject.mixins.Transmogrifier;
 import org.skife.jdbi.v2.sqlobject.stringtemplate.ExternalizedSqlViaStringTemplate3;
 
 import com.ning.billing.analytics.model.BusinessInvoicePayment;
 
 @ExternalizedSqlViaStringTemplate3()
 @RegisterMapper(BusinessInvoicePaymentMapper.class)
-public interface BusinessInvoicePaymentSqlDao {
+public interface BusinessInvoicePaymentSqlDao extends Transactional<BusinessInvoicePaymentSqlDao>, Transmogrifier {
     @SqlQuery
-    BusinessInvoicePayment getInvoicePaymentForPaymentAttempt(@Bind("attempt_id") final String attemptId);
-
-    @SqlQuery
-    List<BusinessInvoicePayment> getInvoicePaymentsForPayment(@Bind("payment_id") final String paymentId);
+    BusinessInvoicePayment getInvoicePayment(@Bind("payment_id") final String paymentId);
 
     @SqlQuery
     List<BusinessInvoicePayment> getInvoicePaymentsForAccountByKey(@Bind("account_key") final String accountKey);
@@ -42,7 +41,7 @@ public interface BusinessInvoicePaymentSqlDao {
     int createInvoicePayment(@BusinessInvoicePaymentBinder final BusinessInvoicePayment payment);
 
     @SqlUpdate
-    int deleteInvoicePaymentForPaymentAttempt(@Bind("attempt_id") final String attemptId);
+    int deleteInvoicePayment(@Bind("payment_id") final String paymentId);
 
     @SqlUpdate
     void test();
diff --git a/analytics/src/main/java/com/ning/billing/analytics/dao/DefaultAnalyticsDao.java b/analytics/src/main/java/com/ning/billing/analytics/dao/DefaultAnalyticsDao.java
index c7b40d8..d4855fd 100644
--- a/analytics/src/main/java/com/ning/billing/analytics/dao/DefaultAnalyticsDao.java
+++ b/analytics/src/main/java/com/ning/billing/analytics/dao/DefaultAnalyticsDao.java
@@ -23,6 +23,7 @@ import com.ning.billing.analytics.model.BusinessAccount;
 import com.ning.billing.analytics.model.BusinessAccountTag;
 import com.ning.billing.analytics.model.BusinessInvoice;
 import com.ning.billing.analytics.model.BusinessInvoiceItem;
+import com.ning.billing.analytics.model.BusinessInvoicePayment;
 import com.ning.billing.analytics.model.BusinessOverdueStatus;
 import com.ning.billing.analytics.model.BusinessSubscriptionTransition;
 
@@ -33,6 +34,7 @@ public class DefaultAnalyticsDao implements AnalyticsDao {
     private final BusinessInvoiceItemSqlDao invoiceItemSqlDao;
     private final BusinessAccountTagSqlDao accountTagSqlDao;
     private final BusinessOverdueStatusSqlDao overdueStatusSqlDao;
+    private final BusinessInvoicePaymentSqlDao invoicePaymentSqlDao;
 
     @Inject
     public DefaultAnalyticsDao(final BusinessAccountSqlDao accountSqlDao,
@@ -40,13 +42,15 @@ public class DefaultAnalyticsDao implements AnalyticsDao {
                                final BusinessInvoiceSqlDao invoiceSqlDao,
                                final BusinessInvoiceItemSqlDao invoiceItemSqlDao,
                                final BusinessAccountTagSqlDao accountTagSqlDao,
-                               final BusinessOverdueStatusSqlDao overdueStatusSqlDao) {
+                               final BusinessOverdueStatusSqlDao overdueStatusSqlDao,
+                               final BusinessInvoicePaymentSqlDao invoicePaymentSqlDao) {
         this.accountSqlDao = accountSqlDao;
         this.subscriptionTransitionSqlDao = subscriptionTransitionSqlDao;
         this.invoiceSqlDao = invoiceSqlDao;
         this.invoiceItemSqlDao = invoiceItemSqlDao;
         this.accountTagSqlDao = accountTagSqlDao;
         this.overdueStatusSqlDao = overdueStatusSqlDao;
+        this.invoicePaymentSqlDao = invoicePaymentSqlDao;
     }
 
     @Override
@@ -78,4 +82,9 @@ public class DefaultAnalyticsDao implements AnalyticsDao {
     public List<BusinessOverdueStatus> getOverdueStatusesForBundleByKey(final String externalKey) {
         return overdueStatusSqlDao.getOverdueStatusesForBundleByKey(externalKey);
     }
+
+    @Override
+    public List<BusinessInvoicePayment> getInvoicePaymentsForAccountByKey(final String accountKey) {
+        return invoicePaymentSqlDao.getInvoicePaymentsForAccountByKey(accountKey);
+    }
 }
diff --git a/analytics/src/main/java/com/ning/billing/analytics/model/BusinessInvoicePayment.java b/analytics/src/main/java/com/ning/billing/analytics/model/BusinessInvoicePayment.java
index 56e59a4..d3044a8 100644
--- a/analytics/src/main/java/com/ning/billing/analytics/model/BusinessInvoicePayment.java
+++ b/analytics/src/main/java/com/ning/billing/analytics/model/BusinessInvoicePayment.java
@@ -27,7 +27,7 @@ import com.ning.billing.catalog.api.Currency;
 public class BusinessInvoicePayment {
     private final UUID paymentId;
     private final DateTime createdDate;
-    private final UUID attemptId;
+    private final String extPaymentRefId;
     private final DateTime updatedDate;
     private final String accountKey;
     private final UUID invoiceId;
@@ -43,7 +43,7 @@ public class BusinessInvoicePayment {
     private final String cardType;
     private final String cardCountry;
 
-    public BusinessInvoicePayment(final String accountKey, final BigDecimal amount, final UUID attemptId,
+    public BusinessInvoicePayment(final String accountKey, final BigDecimal amount, final String extPaymentRefId,
                                   final String cardCountry, final String cardType, final DateTime createdDate,
                                   final Currency currency, final DateTime effectiveDate, final UUID invoiceId,
                                   final String paymentError, final UUID paymentId, final String paymentMethod,
@@ -51,7 +51,7 @@ public class BusinessInvoicePayment {
                                   final BigDecimal requestedAmount, final DateTime updatedDate) {
         this.accountKey = accountKey;
         this.amount = amount;
-        this.attemptId = attemptId;
+        this.extPaymentRefId = extPaymentRefId;
         this.cardCountry = cardCountry;
         this.cardType = cardType;
         this.createdDate = createdDate;
@@ -68,8 +68,8 @@ public class BusinessInvoicePayment {
         this.updatedDate = updatedDate;
     }
 
-    public UUID getAttemptId() {
-        return attemptId;
+    public String getExtPaymentRefId() {
+        return extPaymentRefId;
     }
 
     public DateTime getCreatedDate() {
@@ -143,7 +143,7 @@ public class BusinessInvoicePayment {
         sb.append("{accountKey='").append(accountKey).append('\'');
         sb.append(", paymentId=").append(paymentId);
         sb.append(", createdDate=").append(createdDate);
-        sb.append(", attemptId=").append(attemptId);
+        sb.append(", extPaymentRefId=").append(extPaymentRefId);
         sb.append(", updatedDate=").append(updatedDate);
         sb.append(", invoiceId=").append(invoiceId);
         sb.append(", effectiveDate=").append(effectiveDate);
@@ -178,7 +178,7 @@ public class BusinessInvoicePayment {
         if (amount != null ? Rounder.round(amount) != Rounder.round(that.amount) : that.amount != null) {
             return false;
         }
-        if (attemptId != null ? !attemptId.equals(that.attemptId) : that.attemptId != null) {
+        if (extPaymentRefId != null ? !extPaymentRefId.equals(that.extPaymentRefId) : that.extPaymentRefId != null) {
             return false;
         }
         if (cardCountry != null ? !cardCountry.equals(that.cardCountry) : that.cardCountry != null) {
@@ -231,7 +231,7 @@ public class BusinessInvoicePayment {
     public int hashCode() {
         int result = paymentId != null ? paymentId.hashCode() : 0;
         result = 31 * result + (createdDate != null ? createdDate.hashCode() : 0);
-        result = 31 * result + (attemptId != null ? attemptId.hashCode() : 0);
+        result = 31 * result + (extPaymentRefId != null ? extPaymentRefId.hashCode() : 0);
         result = 31 * result + (updatedDate != null ? updatedDate.hashCode() : 0);
         result = 31 * result + (accountKey != null ? accountKey.hashCode() : 0);
         result = 31 * result + (invoiceId != null ? invoiceId.hashCode() : 0);
diff --git a/analytics/src/main/resources/com/ning/billing/analytics/dao/BusinessInvoicePaymentSqlDao.sql.stg b/analytics/src/main/resources/com/ning/billing/analytics/dao/BusinessInvoicePaymentSqlDao.sql.stg
index 4d87398..a5efb04 100644
--- a/analytics/src/main/resources/com/ning/billing/analytics/dao/BusinessInvoicePaymentSqlDao.sql.stg
+++ b/analytics/src/main/resources/com/ning/billing/analytics/dao/BusinessInvoicePaymentSqlDao.sql.stg
@@ -1,36 +1,11 @@
 group BusinessInvoicePayment;
 
-getInvoicePaymentForPaymentAttempt(attempt_id) ::= <<
+getInvoicePayment(payment_id) ::= <<
 select
   payment_id
 , created_date
 , updated_date
-, attempt_id
-, account_key
-, invoice_id
-, effective_date
-, amount
-, currency
-, payment_error
-, processing_status
-, requested_amount
-, plugin_name
-, payment_type
-, payment_method
-, card_type
-, card_country
-from bip
-where attempt_id = :attempt_id
-limit 1
-;
->>
-
-getInvoicePaymentsForPayment(payment_id) ::= <<
-select
-  payment_id
-, created_date
-, updated_date
-, attempt_id
+, ext_payment_ref_id
 , account_key
 , invoice_id
 , effective_date
@@ -46,16 +21,16 @@ select
 , card_country
 from bip
 where payment_id = :payment_id
-order by created_date asc
+limit 1
 ;
 >>
 
-getInvoicePaymentsForAccount(account_key) ::= <<
+getInvoicePaymentsForAccountByKey(account_key) ::= <<
 select
   payment_id
 , created_date
 , updated_date
-, attempt_id
+, ext_payment_ref_id
 , account_key
 , invoice_id
 , effective_date
@@ -80,7 +55,7 @@ insert into bip (
   payment_id
 , created_date
 , updated_date
-, attempt_id
+, ext_payment_ref_id
 , account_key
 , invoice_id
 , effective_date
@@ -98,7 +73,7 @@ insert into bip (
   :payment_id
 , :created_date
 , :updated_date
-, :attempt_id
+, :ext_payment_ref_id
 , :account_key
 , :invoice_id
 , :effective_date
@@ -115,8 +90,8 @@ insert into bip (
 );
 >>
 
-deleteInvoicePaymentForPaymentAttempt(attempt_id) ::= <<
-delete from bip where attempt_id = :attempt_id
+deleteInvoicePayment(payment_id) ::= <<
+delete from bip where payment_id = :payment_id
 >>
 
 test() ::= <<
diff --git a/analytics/src/main/resources/com/ning/billing/analytics/ddl.sql b/analytics/src/main/resources/com/ning/billing/analytics/ddl.sql
index 09d0a0c..c3095e4 100644
--- a/analytics/src/main/resources/com/ning/billing/analytics/ddl.sql
+++ b/analytics/src/main/resources/com/ning/billing/analytics/ddl.sql
@@ -106,7 +106,7 @@ create table bip (
 , payment_id char(36) not null
 , created_date bigint not null
 , updated_date bigint not null
-, attempt_id char(36) not null
+, ext_payment_ref_id varchar(64) default null
 , account_key varchar(50) not null comment 'Account external key'
 , invoice_id char(36) not null
 , effective_date bigint default null
@@ -121,8 +121,8 @@ create table bip (
 , card_type varchar(20) default null
 , card_country varchar(20) default null
 , primary key(record_id)
-) engine=innodb comment 'Business Invoice Payments, track all payment attempts';
-create unique index bip_key_index on bip (attempt_id);
+) engine=innodb comment 'Business Invoice Payments, track all payments';
+create unique index bip_key_index on bip (payment_id);
 
 drop table if exists bos;
 create table bos (
diff --git a/analytics/src/test/java/com/ning/billing/analytics/dao/TestBusinessInvoicePaymentSqlDao.java b/analytics/src/test/java/com/ning/billing/analytics/dao/TestBusinessInvoicePaymentSqlDao.java
index c7414f6..3f0b762 100644
--- a/analytics/src/test/java/com/ning/billing/analytics/dao/TestBusinessInvoicePaymentSqlDao.java
+++ b/analytics/src/test/java/com/ning/billing/analytics/dao/TestBusinessInvoicePaymentSqlDao.java
@@ -41,60 +41,53 @@ public class TestBusinessInvoicePaymentSqlDao extends TestWithEmbeddedDB {
 
     @Test(groups = "slow")
     public void testCRUD() throws Exception {
-        final UUID attemptId = UUID.randomUUID();
+        final String extPaymentRefId = UUID.randomUUID().toString();
         final String accountKey = UUID.randomUUID().toString();
-        final BusinessInvoicePayment invoicePayment = createInvoicePayment(attemptId, accountKey);
+        final BusinessInvoicePayment invoicePayment = createInvoicePayment(extPaymentRefId, accountKey);
 
         // Verify initial state
-        Assert.assertNull(invoicePaymentSqlDao.getInvoicePaymentForPaymentAttempt(invoicePayment.getAttemptId().toString()));
-        Assert.assertEquals(invoicePaymentSqlDao.deleteInvoicePaymentForPaymentAttempt(invoicePayment.getAttemptId().toString()), 0);
+        Assert.assertNull(invoicePaymentSqlDao.getInvoicePayment(invoicePayment.getPaymentId().toString()));
+        Assert.assertEquals(invoicePaymentSqlDao.getInvoicePaymentsForAccountByKey(invoicePayment.getAccountKey()).size(), 0);
 
         // Add the invoice payment
         Assert.assertEquals(invoicePaymentSqlDao.createInvoicePayment(invoicePayment), 1);
 
         // Retrieve it
-        Assert.assertEquals(invoicePaymentSqlDao.getInvoicePaymentForPaymentAttempt(invoicePayment.getAttemptId().toString()), invoicePayment);
+        Assert.assertEquals(invoicePaymentSqlDao.getInvoicePayment(invoicePayment.getPaymentId().toString()), invoicePayment);
         Assert.assertEquals(invoicePaymentSqlDao.getInvoicePaymentsForAccountByKey(invoicePayment.getAccountKey()).size(), 1);
         Assert.assertEquals(invoicePaymentSqlDao.getInvoicePaymentsForAccountByKey(invoicePayment.getAccountKey()).get(0), invoicePayment);
-        Assert.assertEquals(invoicePaymentSqlDao.getInvoicePaymentsForPayment(invoicePayment.getPaymentId().toString()).size(), 1);
-        Assert.assertEquals(invoicePaymentSqlDao.getInvoicePaymentsForPayment(invoicePayment.getPaymentId().toString()).get(0), invoicePayment);
 
         // Delete it
-        Assert.assertEquals(invoicePaymentSqlDao.deleteInvoicePaymentForPaymentAttempt(invoicePayment.getAttemptId().toString()), 1);
-        Assert.assertNull(invoicePaymentSqlDao.getInvoicePaymentForPaymentAttempt(invoicePayment.getAttemptId().toString()));
+        Assert.assertEquals(invoicePaymentSqlDao.deleteInvoicePayment(invoicePayment.getPaymentId().toString()), 1);
+        Assert.assertNull(invoicePaymentSqlDao.getInvoicePayment(invoicePayment.getPaymentId().toString()));
         Assert.assertEquals(invoicePaymentSqlDao.getInvoicePaymentsForAccountByKey(invoicePayment.getAccountKey()).size(), 0);
-        Assert.assertEquals(invoicePaymentSqlDao.getInvoicePaymentsForPayment(invoicePayment.getPaymentId().toString()).size(), 0);
     }
 
     @Test(groups = "slow")
     public void testSegmentation() throws Exception {
-        final UUID attemptId1 = UUID.randomUUID();
+        final String extPaymentRefId1 = UUID.randomUUID().toString();
         final String accountKey1 = UUID.randomUUID().toString();
-        final BusinessInvoicePayment invoicePayment1 = createInvoicePayment(attemptId1, accountKey1);
-        final UUID attemptId2 = UUID.randomUUID();
+        final BusinessInvoicePayment invoicePayment1 = createInvoicePayment(extPaymentRefId1, accountKey1);
+        final String extPaymentRefId2 = UUID.randomUUID().toString();
         final String accountKey2 = UUID.randomUUID().toString();
-        final BusinessInvoicePayment invoicePayment2 = createInvoicePayment(attemptId2, accountKey2);
+        final BusinessInvoicePayment invoicePayment2 = createInvoicePayment(extPaymentRefId2, accountKey2);
 
         // Create both invoice payments
         Assert.assertEquals(invoicePaymentSqlDao.createInvoicePayment(invoicePayment1), 1);
         Assert.assertEquals(invoicePaymentSqlDao.createInvoicePayment(invoicePayment2), 1);
 
-        Assert.assertEquals(invoicePaymentSqlDao.getInvoicePaymentForPaymentAttempt(invoicePayment1.getAttemptId().toString()), invoicePayment1);
-        Assert.assertEquals(invoicePaymentSqlDao.getInvoicePaymentForPaymentAttempt(invoicePayment2.getAttemptId().toString()), invoicePayment2);
+        Assert.assertEquals(invoicePaymentSqlDao.getInvoicePayment(invoicePayment1.getPaymentId().toString()), invoicePayment1);
+        Assert.assertEquals(invoicePaymentSqlDao.getInvoicePayment(invoicePayment2.getPaymentId().toString()), invoicePayment2);
         Assert.assertEquals(invoicePaymentSqlDao.getInvoicePaymentsForAccountByKey(invoicePayment1.getAccountKey()).size(), 1);
         Assert.assertEquals(invoicePaymentSqlDao.getInvoicePaymentsForAccountByKey(invoicePayment2.getAccountKey()).size(), 1);
-        Assert.assertEquals(invoicePaymentSqlDao.getInvoicePaymentsForPayment(invoicePayment1.getPaymentId().toString()).size(), 1);
-        Assert.assertEquals(invoicePaymentSqlDao.getInvoicePaymentsForPayment(invoicePayment2.getPaymentId().toString()).size(), 1);
 
         // Remove the first invoice payment
-        Assert.assertEquals(invoicePaymentSqlDao.deleteInvoicePaymentForPaymentAttempt(invoicePayment1.getAttemptId().toString()), 1);
+        Assert.assertEquals(invoicePaymentSqlDao.deleteInvoicePayment(invoicePayment1.getPaymentId().toString()), 1);
 
-        Assert.assertNull(invoicePaymentSqlDao.getInvoicePaymentForPaymentAttempt(invoicePayment1.getAttemptId().toString()));
-        Assert.assertEquals(invoicePaymentSqlDao.getInvoicePaymentForPaymentAttempt(invoicePayment2.getAttemptId().toString()), invoicePayment2);
+        Assert.assertNull(invoicePaymentSqlDao.getInvoicePayment(invoicePayment1.getPaymentId().toString()));
+        Assert.assertEquals(invoicePaymentSqlDao.getInvoicePayment(invoicePayment2.getPaymentId().toString()), invoicePayment2);
         Assert.assertEquals(invoicePaymentSqlDao.getInvoicePaymentsForAccountByKey(invoicePayment1.getAccountKey()).size(), 0);
         Assert.assertEquals(invoicePaymentSqlDao.getInvoicePaymentsForAccountByKey(invoicePayment2.getAccountKey()).size(), 1);
-        Assert.assertEquals(invoicePaymentSqlDao.getInvoicePaymentsForPayment(invoicePayment1.getPaymentId().toString()).size(), 0);
-        Assert.assertEquals(invoicePaymentSqlDao.getInvoicePaymentsForPayment(invoicePayment2.getPaymentId().toString()).size(), 1);
     }
 
     @Test(groups = "slow")
@@ -107,7 +100,7 @@ public class TestBusinessInvoicePaymentSqlDao extends TestWithEmbeddedDB {
         }
     }
 
-    private BusinessInvoicePayment createInvoicePayment(final UUID attemptId, final String accountKey) {
+    private BusinessInvoicePayment createInvoicePayment(final String extPaymentRefId, final String accountKey) {
         final BigDecimal amount = BigDecimal.ONE;
         final String cardCountry = UUID.randomUUID().toString().substring(0, 20);
         final String cardType = UUID.randomUUID().toString().substring(0, 20);
@@ -124,7 +117,7 @@ public class TestBusinessInvoicePaymentSqlDao extends TestWithEmbeddedDB {
         final BigDecimal requestedAmount = BigDecimal.ZERO;
         final DateTime updatedDate = new DateTime(DateTimeZone.UTC);
 
-        return new BusinessInvoicePayment(accountKey, amount, attemptId,
+        return new BusinessInvoicePayment(accountKey, amount, extPaymentRefId,
                                           cardCountry, cardType, createdDate,
                                           currency, effectiveDate, invoiceId,
                                           paymentError, paymentId, paymentMethod,
diff --git a/analytics/src/test/java/com/ning/billing/analytics/model/TestBusinessInvoicePayment.java b/analytics/src/test/java/com/ning/billing/analytics/model/TestBusinessInvoicePayment.java
index 5b4b7f2..e5a997b 100644
--- a/analytics/src/test/java/com/ning/billing/analytics/model/TestBusinessInvoicePayment.java
+++ b/analytics/src/test/java/com/ning/billing/analytics/model/TestBusinessInvoicePayment.java
@@ -32,7 +32,7 @@ public class TestBusinessInvoicePayment extends AnalyticsTestSuite {
     public void testEquals() throws Exception {
         final String accountKey = UUID.randomUUID().toString();
         final BigDecimal amount = BigDecimal.ONE;
-        final UUID attemptId = UUID.randomUUID();
+        final String extPaymentRefId = UUID.randomUUID().toString();
         final String cardCountry = UUID.randomUUID().toString();
         final String cardType = UUID.randomUUID().toString();
         final DateTime createdDate = new DateTime(DateTimeZone.UTC);
@@ -47,7 +47,7 @@ public class TestBusinessInvoicePayment extends AnalyticsTestSuite {
         final String processingStatus = UUID.randomUUID().toString();
         final BigDecimal requestedAmount = BigDecimal.ZERO;
         final DateTime updatedDate = new DateTime(DateTimeZone.UTC);
-        final BusinessInvoicePayment invoicePayment = new BusinessInvoicePayment(accountKey, amount, attemptId,
+        final BusinessInvoicePayment invoicePayment = new BusinessInvoicePayment(accountKey, amount, extPaymentRefId,
                                                                                  cardCountry, cardType, createdDate,
                                                                                  currency, effectiveDate, invoiceId,
                                                                                  paymentError, paymentId, paymentMethod,
@@ -58,7 +58,7 @@ public class TestBusinessInvoicePayment extends AnalyticsTestSuite {
         Assert.assertTrue(invoicePayment.equals(invoicePayment));
         Assert.assertEquals(invoicePayment.getAccountKey(), accountKey);
         Assert.assertEquals(invoicePayment.getAmount(), amount);
-        Assert.assertEquals(invoicePayment.getAttemptId(), attemptId);
+        Assert.assertEquals(invoicePayment.getExtPaymentRefId(), extPaymentRefId);
         Assert.assertEquals(invoicePayment.getCardCountry(), cardCountry);
         Assert.assertEquals(invoicePayment.getCardType(), cardType);
         Assert.assertEquals(invoicePayment.getCreatedDate(), createdDate);
@@ -74,7 +74,7 @@ public class TestBusinessInvoicePayment extends AnalyticsTestSuite {
         Assert.assertEquals(invoicePayment.getRequestedAmount(), requestedAmount);
         Assert.assertEquals(invoicePayment.getUpdatedDate(), updatedDate);
 
-        final BusinessInvoicePayment otherInvoicePayment = new BusinessInvoicePayment(null, null, attemptId, null, null, createdDate,
+        final BusinessInvoicePayment otherInvoicePayment = new BusinessInvoicePayment(null, null, extPaymentRefId, null, null, createdDate,
                                                                                       null, null, null, null, paymentId, null,
                                                                                       null, null, null, null, null);
         Assert.assertFalse(invoicePayment.equals(otherInvoicePayment));
diff --git a/beatrix/src/test/java/com/ning/billing/beatrix/integration/TestAnalytics.java b/beatrix/src/test/java/com/ning/billing/beatrix/integration/TestAnalytics.java
index cc7b531..adc0fc6 100644
--- a/beatrix/src/test/java/com/ning/billing/beatrix/integration/TestAnalytics.java
+++ b/beatrix/src/test/java/com/ning/billing/beatrix/integration/TestAnalytics.java
@@ -53,6 +53,7 @@ import com.ning.billing.entitlement.api.user.EntitlementUserApiException;
 import com.ning.billing.entitlement.api.user.Subscription;
 import com.ning.billing.entitlement.api.user.SubscriptionBundle;
 import com.ning.billing.overdue.config.OverdueConfig;
+import com.ning.billing.payment.api.PaymentStatus;
 import com.ning.billing.util.api.TagApiException;
 import com.ning.billing.util.api.TagDefinitionApiException;
 import com.ning.billing.util.config.XMLLoader;
@@ -199,6 +200,13 @@ public class TestAnalytics extends TestIntegrationBase {
         final Subscription subscription = verifyFirstSubscription(account, bundle);
         assertTrue(busHandler.isCompleted(DELAY));
 
+        // Verify the initial state of payments
+        Assert.assertEquals(analyticsUserApi.getInvoicePaymentsForAccount(account.getExternalKey()).size(), 0);
+
+        // Verify the account payment fields
+        Assert.assertEquals(analyticsUserApi.getAccountByKey(account.getExternalKey()).getBalance().doubleValue(), Rounder.round(BigDecimal.ZERO));
+        Assert.assertNull(analyticsUserApi.getAccountByKey(account.getExternalKey()).getLastPaymentStatus());
+
         // Verify the initial overdue status
         Assert.assertEquals(analyticsUserApi.getOverdueStatusesForBundle(bundle.getKey()).size(), 0);
 
@@ -206,10 +214,29 @@ public class TestAnalytics extends TestIntegrationBase {
         busHandler.pushExpectedEvents(TestApiListener.NextEvent.PHASE, TestApiListener.NextEvent.INVOICE, TestApiListener.NextEvent.PAYMENT_ERROR);
         clock.addDays(30); // DAY 30 have to get out of trial before first payment
         Assert.assertTrue(busHandler.isCompleted(DELAY));
+        waitALittle();
 
         // Check BST - nothing should have changed
         verifyBSTWithTrialAndEvergreenPhases(account, bundle, subscription);
 
+        // Verify the payments - we should have received one
+        Assert.assertEquals(analyticsUserApi.getInvoicePaymentsForAccount(account.getExternalKey()).size(), 1);
+        Assert.assertEquals(analyticsUserApi.getInvoicePaymentsForAccount(account.getExternalKey()).get(0).getAccountKey(), account.getExternalKey());
+        Assert.assertTrue(analyticsUserApi.getInvoicePaymentsForAccount(account.getExternalKey()).get(0).getAmount().compareTo(BigDecimal.ZERO) > 0);
+        Assert.assertTrue(analyticsUserApi.getInvoicePaymentsForAccount(account.getExternalKey()).get(0).getRequestedAmount().compareTo(BigDecimal.ZERO) > 0);
+        Assert.assertNull(analyticsUserApi.getInvoicePaymentsForAccount(account.getExternalKey()).get(0).getExtPaymentRefId());
+        Assert.assertEquals(analyticsUserApi.getInvoicePaymentsForAccount(account.getExternalKey()).get(0).getProcessingStatus(), PaymentStatus.PAYMENT_FAILURE.toString());
+        Assert.assertEquals(analyticsUserApi.getInvoicePaymentsForAccount(account.getExternalKey()).get(0).getPluginName(), BeatrixModule.PLUGIN_NAME);
+
+        // Verify the account object has been updated
+        Assert.assertEquals(analyticsUserApi.getAccountByKey(account.getExternalKey()).getBalance(),
+                            analyticsUserApi.getInvoicePaymentsForAccount(account.getExternalKey()).get(0).getAmount());
+
+        // Verify the invoice balance isn't zero and is equal to the payment amount (don't look at the first, trial, invoice)
+        Assert.assertTrue(analyticsUserApi.getInvoicesForAccount(account.getExternalKey()).get(1).getBalance().compareTo(BigDecimal.ZERO) > 0);
+        Assert.assertEquals(analyticsUserApi.getInvoicesForAccount(account.getExternalKey()).get(1).getBalance(),
+                            analyticsUserApi.getInvoicePaymentsForAccount(account.getExternalKey()).get(0).getAmount());
+
         // Verify overdue status - we should still be in clear state
         Assert.assertEquals(analyticsUserApi.getOverdueStatusesForBundle(bundle.getKey()).size(), 0);