killbill-aplcache

Merge branch 'master' of github.com:ning/killbill into inv-ent-integration

1/24/2012 9:00:00 PM

Changes

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

api/pom.xml 10(+9 -1)

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

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

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

payment/pom.xml 100(+97 -3)

pom.xml 79(+66 -13)

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

Details

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

diff --git a/account/pom.xml b/account/pom.xml
index 44b4543..0146205 100644
--- a/account/pom.xml
+++ b/account/pom.xml
@@ -13,7 +13,7 @@
     <parent>
         <groupId>com.ning.billing</groupId>
         <artifactId>killbill</artifactId>
-        <version>0.1.2-SNAPSHOT</version>
+        <version>0.1.3-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>killbill-account</artifactId>
diff --git a/account/src/main/java/com/ning/billing/account/api/DefaultAccount.java b/account/src/main/java/com/ning/billing/account/api/DefaultAccount.java
index b8c4314..0f9d4b5 100644
--- a/account/src/main/java/com/ning/billing/account/api/DefaultAccount.java
+++ b/account/src/main/java/com/ning/billing/account/api/DefaultAccount.java
@@ -19,6 +19,9 @@ package com.ning.billing.account.api;
 import java.math.BigDecimal;
 import java.util.List;
 import java.util.UUID;
+
+import com.google.inject.Inject;
+import com.ning.billing.util.clock.Clock;
 import org.joda.time.DateTime;
 import com.ning.billing.catalog.api.Currency;
 import com.ning.billing.util.customfield.CustomizableEntityBase;
@@ -40,23 +43,41 @@ public class DefaultAccount extends CustomizableEntityBase implements Account {
     private final String paymentProviderName;
     private final BigDecimal balance;
     private final DefaultTagStore tags;
+    private final DateTime createdDate;
+    private final DateTime updatedDate;
+
+    @Inject
+    private Clock clock;
 
     public DefaultAccount(final AccountData data) {
         this(UUID.randomUUID(), data.getExternalKey(), data.getEmail(), data.getName(),
                 data.getFirstNameLength(), data.getPhone(), data.getCurrency(), data.getBillCycleDay(),
-                data.getPaymentProviderName(), BigDecimal.ZERO);
+                data.getPaymentProviderName(), BigDecimal.ZERO, null, null);
     }
 
     public DefaultAccount(final UUID id, final AccountData data) {
         this(id, data.getExternalKey(), data.getEmail(), data.getName(),
                 data.getFirstNameLength(), data.getPhone(), data.getCurrency(), data.getBillCycleDay(),
-                data.getPaymentProviderName(), BigDecimal.ZERO);
+                data.getPaymentProviderName(), BigDecimal.ZERO, null, null);
+    }
+
+    public DefaultAccount(UUID id,
+                          String externalKey,
+                          String email,
+                          String name,
+                          int firstNameLength,
+                          String phone,
+                          Currency currency,
+                          int billCycleDay,
+                          String paymentProviderName,
+                          BigDecimal balance) {
+        this(id, externalKey, email, name, firstNameLength, phone, currency, billCycleDay, paymentProviderName, balance, null, null);
     }
 
     public DefaultAccount(final UUID id, final String externalKey, final String email,
                           final String name, final int firstNameLength,
                           final String phone, final Currency currency, final int billCycleDay, final String paymentProviderName,
-                          final BigDecimal balance) {
+                          final BigDecimal balance, final DateTime createdDate, final DateTime updatedDate) {
         super(id);
         this.externalKey = externalKey;
         this.email = email;
@@ -67,6 +88,8 @@ public class DefaultAccount extends CustomizableEntityBase implements Account {
         this.billCycleDay = billCycleDay;
         this.paymentProviderName = paymentProviderName;
         this.balance = balance;
+        this.createdDate = (createdDate == null) ? clock.getUTCNow() : createdDate;
+        this.updatedDate = (updatedDate == null) ? clock.getUTCNow() : updatedDate;
 
         this.tags = new DefaultTagStore(id, getObjectName());
     }
@@ -117,6 +140,16 @@ public class DefaultAccount extends CustomizableEntityBase implements Account {
     }
 
     @Override
+    public DateTime getCreatedDate() {
+        return createdDate;
+    }
+
+    @Override
+    public DateTime getUpdatedDate() {
+        return updatedDate;
+    }
+
+    @Override
     public List<Tag> getTagList() {
         return tags.getEntityList();
     }
@@ -163,4 +196,9 @@ public class DefaultAccount extends CustomizableEntityBase implements Account {
     public BigDecimal getBalance() {
         return balance;
     }
+
+    @Override
+    public String toString() {
+        return "DefaultAccount [externalKey=" + externalKey + ", email=" + email + ", name=" + name + ", firstNameLength=" + firstNameLength + ", phone=" + phone + ", currency=" + currency + ", billCycleDay=" + billCycleDay + ", paymentProviderName=" + paymentProviderName + ", balance=" + balance + ", tags=" + tags + ", createdDate=" + createdDate + ", updatedDate=" + updatedDate + "]";
+    }
 }
\ No newline at end of file
diff --git a/account/src/main/java/com/ning/billing/account/api/user/AccountBuilder.java b/account/src/main/java/com/ning/billing/account/api/user/AccountBuilder.java
index e05e967..c2866f7 100644
--- a/account/src/main/java/com/ning/billing/account/api/user/AccountBuilder.java
+++ b/account/src/main/java/com/ning/billing/account/api/user/AccountBuilder.java
@@ -16,14 +16,16 @@
 
 package com.ning.billing.account.api.user;
 
-import com.ning.billing.account.api.DefaultAccount;
-import com.ning.billing.catalog.api.Currency;
-
 import java.math.BigDecimal;
 import java.util.UUID;
 
+import org.joda.time.DateTime;
+
+import com.ning.billing.account.api.DefaultAccount;
+import com.ning.billing.catalog.api.Currency;
+
 public class AccountBuilder {
-    private UUID id;
+    private final UUID id;
     private String externalKey;
     private String email;
     private String name;
@@ -33,6 +35,8 @@ public class AccountBuilder {
     private int billingCycleDay;
     private String paymentProviderName;
     private BigDecimal balance;
+    private DateTime createdDate;
+    private DateTime updatedDate;
 
     public AccountBuilder() {
         this(UUID.randomUUID());
@@ -87,8 +91,19 @@ public class AccountBuilder {
         return this;
     }
 
+    public AccountBuilder createdDate(DateTime createdDate) {
+        this.createdDate = createdDate;
+        return this;
+    }
+
+    public AccountBuilder updatedDate(DateTime updatedDate) {
+        this.updatedDate = updatedDate;
+        return this;
+    }
+
     public DefaultAccount build() {
         return new DefaultAccount(id, externalKey, email, name, firstNameLength,
-                                  phone, currency, billingCycleDay, paymentProviderName, balance);
+                                  phone, currency, billingCycleDay, paymentProviderName,
+                                  balance, createdDate, updatedDate);
     }
 }
diff --git a/account/src/main/java/com/ning/billing/account/dao/AccountSqlDao.java b/account/src/main/java/com/ning/billing/account/dao/AccountSqlDao.java
index 21122e2..978872c 100644
--- a/account/src/main/java/com/ning/billing/account/dao/AccountSqlDao.java
+++ b/account/src/main/java/com/ning/billing/account/dao/AccountSqlDao.java
@@ -16,12 +16,19 @@
 
 package com.ning.billing.account.dao;
 
-import com.ning.billing.account.api.Account;
-import com.ning.billing.account.api.AccountApiException;
-import com.ning.billing.account.api.user.AccountBuilder;
-import com.ning.billing.catalog.api.Currency;
-import com.ning.billing.util.UuidMapper;
-import com.ning.billing.util.entity.EntityDao;
+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.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Timestamp;
+import java.util.Date;
+import java.util.UUID;
+
+import org.joda.time.DateTime;
+import org.joda.time.DateTimeZone;
 import org.skife.jdbi.v2.SQLStatement;
 import org.skife.jdbi.v2.StatementContext;
 import org.skife.jdbi.v2.sqlobject.Bind;
@@ -36,15 +43,11 @@ 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.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.UUID;
+import com.ning.billing.account.api.Account;
+import com.ning.billing.account.api.user.AccountBuilder;
+import com.ning.billing.catalog.api.Currency;
+import com.ning.billing.util.UuidMapper;
+import com.ning.billing.util.entity.EntityDao;
 
 @ExternalizedSqlViaStringTemplate3
 @RegisterMapper({UuidMapper.class, AccountSqlDao.AccountMapper.class})
@@ -64,8 +67,15 @@ public interface AccountSqlDao extends EntityDao<Account>, Transactional<Account
     public void update(@AccountBinder Account account);
 
     public static class AccountMapper implements ResultSetMapper<Account> {
+
+        private DateTime getDate(ResultSet rs, String fieldName) throws SQLException {
+            final Timestamp resultStamp = rs.getTimestamp(fieldName);
+            return rs.wasNull() ? null : new DateTime(resultStamp).toDateTime(DateTimeZone.UTC);
+        }
+
         @Override
         public Account map(int index, ResultSet result, StatementContext context) throws SQLException {
+
             UUID id = UUID.fromString(result.getString("id"));
             String externalKey = result.getString("external_key");
             String email = result.getString("email");
@@ -76,12 +86,16 @@ public interface AccountSqlDao extends EntityDao<Account>, Transactional<Account
             String currencyString = result.getString("currency");
             Currency currency = (currencyString == null) ? null : Currency.valueOf(currencyString);
             String paymentProviderName = result.getString("payment_provider_name");
+            DateTime createdDate = getDate(result, "created_dt");
+            DateTime updatedDate = getDate(result, "updated_dt");
 
             return new AccountBuilder(id).externalKey(externalKey).email(email)
                                          .name(name).firstNameLength(firstNameLength)
                                          .phone(phone).currency(currency)
                                          .billingCycleDay(billingCycleDay)
                                          .paymentProviderName(paymentProviderName)
+                                         .createdDate(createdDate)
+                                         .updatedDate(updatedDate)
                                          .build();
         }
     }
@@ -91,8 +105,14 @@ public interface AccountSqlDao extends EntityDao<Account>, Transactional<Account
     @Target({ElementType.PARAMETER})
     public @interface AccountBinder {
         public static class AccountBinderFactory implements BinderFactory {
+            @Override
             public Binder build(Annotation annotation) {
                 return new Binder<AccountBinder, Account>() {
+                    private Date getDate(DateTime dateTime) {
+                        return dateTime == null ? null : dateTime.toDate();
+                    }
+
+                    @Override
                     public void bind(SQLStatement q, AccountBinder bind, Account account) {
                         q.bind("id", account.getId().toString());
                         q.bind("externalKey", account.getExternalKey());
@@ -104,6 +124,8 @@ public interface AccountSqlDao extends EntityDao<Account>, Transactional<Account
                         q.bind("currency", (currency == null) ? null : currency.toString());
                         q.bind("billingCycleDay", account.getBillCycleDay());
                         q.bind("paymentProviderName", account.getPaymentProviderName());
+                        q.bind("createdDate", getDate(account.getCreatedDate()));
+                        q.bind("updatedDate", getDate(account.getUpdatedDate()));
                     }
                 };
             }
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 345e2a1..f6e51f4 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
@@ -16,15 +16,15 @@
 
 package com.ning.billing.account.glue;
 
+import org.skife.config.ConfigurationObjectFactory;
+
 import com.google.inject.AbstractModule;
 import com.ning.billing.account.api.AccountService;
 import com.ning.billing.account.api.AccountUserApi;
 import com.ning.billing.account.api.DefaultAccountService;
 import com.ning.billing.account.api.user.DefaultAccountUserApi;
 import com.ning.billing.account.dao.AccountDao;
-import com.ning.billing.account.dao.AccountSqlDao;
 import com.ning.billing.account.dao.DefaultAccountDao;
-import org.skife.config.ConfigurationObjectFactory;
 
 public class AccountModule extends AbstractModule {
 
@@ -33,11 +33,11 @@ public class AccountModule extends AbstractModule {
         bind(AccountConfig.class).toInstance(config);
     }
 
-    private void installAccountDao() {
+    protected void installAccountDao() {
         bind(AccountDao.class).to(DefaultAccountDao.class).asEagerSingleton();
     }
 
-    private void installAccountUserApi() {
+    protected void installAccountUserApi() {
         bind(AccountUserApi.class).to(DefaultAccountUserApi.class).asEagerSingleton();
     }
 
diff --git a/account/src/main/resources/com/ning/billing/account/dao/AccountSqlDao.sql.stg b/account/src/main/resources/com/ning/billing/account/dao/AccountSqlDao.sql.stg
index 7c0e76e..412bcbf 100644
--- a/account/src/main/resources/com/ning/billing/account/dao/AccountSqlDao.sql.stg
+++ b/account/src/main/resources/com/ning/billing/account/dao/AccountSqlDao.sql.stg
@@ -1,45 +1,62 @@
 group AccountDaoSql;
 
+accountFields(prefix) ::= <<
+    <prefix>id,
+    <prefix>external_key,
+    <prefix>email,
+    <prefix>name,
+    <prefix>first_name_length,
+    <prefix>phone,
+    <prefix>currency,
+    <prefix>billing_cycle_day,
+    <prefix>payment_provider_name,
+    <prefix>created_dt,
+    <prefix>updated_dt
+>>
+
+
 create() ::= <<
-    INSERT INTO accounts
-    (id, external_key, email, name, first_name_length, phone, currency, billing_cycle_day, payment_provider_name)
-    VALUES
-    (:id, :externalKey, :email, :name, :firstNameLength, :phone, :currency, :billingCycleDay, :paymentProviderName);
+    INSERT INTO accounts (<accountFields()>)
+    VALUES (:id, :externalKey, :email, :name, :firstNameLength, :phone, :currency, :billingCycleDay, :paymentProviderName, :createdDate, :updatedDate);
 >>
 
 update() ::= <<
     UPDATE accounts
-    SET email = :email, name = :name, first_name_length = :firstNameLength, phone = :phone,
-        currency = :currency, billing_cycle_day = :billingCycleDay, payment_provider_name = :paymentProviderName
+    SET email = :email, 
+        name = :name, 
+        first_name_length = :firstNameLength, 
+        phone = :phone,
+        currency = :currency, 
+        billing_cycle_day = :billingCycleDay, 
+        payment_provider_name = :paymentProviderName,
+        updated_dt = NOW()
     WHERE id = :id;
 >>
 
 getAccountByKey() ::= <<
-    select id, external_key, email, name, first_name_length, phone, currency, billing_cycle_day, payment_provider_name
+    select <accountFields()>
     from accounts
     where external_key = :externalKey;
 >>
 
 getById() ::= <<
-    select
-      a.id, a.external_key, a.email, a.name, a.first_name_length,
-      a.phone, a.currency, a.billing_cycle_day, a.payment_provider_name
-    from accounts a
-    where a.id = :id;
+    SELECT <accountFields()>
+      FROM accounts
+     WHERE id = :id;
 >>
 
 get() ::= <<
-    select id, external_key, email, name, first_name_length, phone, currency, billing_cycle_day, payment_provider_name
-    from accounts;
+    SELECT <accountFields()>
+      FROM accounts;
 >>
 
 getIdFromKey() ::= <<
-    select id
-    from accounts
-    where external_key = :externalKey;
+    SELECT id
+      FROM accounts
+     WHERE external_key = :externalKey;
 >>
 
 test() ::= <<
-    select 1 from accounts;
+    SELECT 1 FROM accounts;
 >>
 ;
\ No newline at end of file
diff --git a/account/src/main/resources/com/ning/billing/account/ddl.sql b/account/src/main/resources/com/ning/billing/account/ddl.sql
index ccfed72..fff193e 100644
--- a/account/src/main/resources/com/ning/billing/account/ddl.sql
+++ b/account/src/main/resources/com/ning/billing/account/ddl.sql
@@ -9,6 +9,8 @@ CREATE TABLE accounts (
     currency char(3) DEFAULT NULL,
     billing_cycle_day int DEFAULT NULL,
     payment_provider_name varchar(20) DEFAULT NULL,
+    created_dt datetime,
+    updated_dt datetime,
     PRIMARY KEY(id)
 ) ENGINE=innodb;
 CREATE UNIQUE INDEX accounts_external_key ON accounts(external_key);
diff --git a/account/src/test/java/com/ning/billing/account/api/MockAccountUserApi.java b/account/src/test/java/com/ning/billing/account/api/MockAccountUserApi.java
new file mode 100644
index 0000000..2aef1ab
--- /dev/null
+++ b/account/src/test/java/com/ning/billing/account/api/MockAccountUserApi.java
@@ -0,0 +1,93 @@
+/*
+ * 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.account.api;
+
+import java.math.BigDecimal;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.UUID;
+import java.util.concurrent.CopyOnWriteArrayList;
+
+import com.ning.billing.catalog.api.Currency;
+import com.ning.billing.util.customfield.CustomField;
+import com.ning.billing.util.tag.Tag;
+
+public class MockAccountUserApi implements AccountUserApi {
+    private final CopyOnWriteArrayList<Account> accounts = new CopyOnWriteArrayList<Account>();
+
+    public Account createAccount(UUID id,
+                                 String externalKey,
+                                 String email,
+                                 String name,
+                                 int firstNameLength,
+                                 String phone,
+                                 Currency currency,
+                                 int billCycleDay,
+                                 String paymentProviderName,
+                                 BigDecimal balance) {
+        Account result = new DefaultAccount(id, externalKey, email, name, firstNameLength, phone, currency, billCycleDay, paymentProviderName, balance);
+        accounts.add(result);
+        return result;
+    }
+
+    @Override
+    public Account createAccount(AccountData data, List<CustomField> fields, List<Tag> tags) throws AccountApiException {
+        Account result = new DefaultAccount(data);
+        accounts.add(result);
+        return result;
+    }
+
+    @Override
+    public Account getAccountByKey(String key) {
+        for (Account account : accounts) {
+            if (key.equals(account.getExternalKey())) {
+                return account;
+            }
+        }
+        return null;
+    }
+
+    @Override
+    public Account getAccountById(UUID uid) {
+        for (Account account : accounts) {
+            if (uid.equals(account.getId())) {
+                return account;
+            }
+        }
+        return null;
+    }
+
+    @Override
+    public List<Account> getAccounts() {
+        return new ArrayList<Account>(accounts);
+    }
+
+    @Override
+    public UUID getIdFromKey(String externalKey) {
+        for (Account account : accounts) {
+            if (externalKey.equals(account.getExternalKey())) {
+                return account.getId();
+            }
+        }
+        return null;
+    }
+
+    @Override
+    public void updateAccount(Account account) {
+        throw new UnsupportedOperationException();
+    }
+}
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 9dcce76..b6d0ab9 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
@@ -16,22 +16,24 @@
 
 package com.ning.billing.account.dao;
 
+import static org.testng.Assert.fail;
+
 import java.io.IOException;
+
 import org.apache.commons.io.IOUtils;
 import org.skife.jdbi.v2.IDBI;
 import org.testng.annotations.AfterClass;
 import org.testng.annotations.BeforeClass;
+
 import com.google.inject.Guice;
 import com.google.inject.Injector;
 import com.google.inject.Stage;
-import com.ning.billing.account.glue.AccountModuleMock;
-import com.ning.billing.util.eventbus.BusService;
+import com.ning.billing.account.glue.AccountModuleWithEmbeddedDb;
 import com.ning.billing.util.eventbus.DefaultEventBusService;
-
-import static org.testng.Assert.fail;
+import com.ning.billing.util.eventbus.BusService;
 
 public abstract class AccountDaoTestBase {
-    protected AccountModuleMock module;
+    protected AccountModuleWithEmbeddedDb module;
     protected AccountDao accountDao;
     protected IDBI dbi;
 
@@ -39,7 +41,7 @@ public abstract class AccountDaoTestBase {
     protected void setup() throws IOException {
         // Health check test to make sure MySQL is setup properly
         try {
-            module = new AccountModuleMock();
+            module = new AccountModuleWithEmbeddedDb();
             final String accountDdl = IOUtils.toString(AccountSqlDao.class.getResourceAsStream("/com/ning/billing/account/ddl.sql"));
             final String utilDdl = IOUtils.toString(AccountSqlDao.class.getResourceAsStream("/com/ning/billing/util/ddl.sql"));
 
diff --git a/account/src/test/java/com/ning/billing/account/dao/MockAccountDao.java b/account/src/test/java/com/ning/billing/account/dao/MockAccountDao.java
new file mode 100644
index 0000000..27643c7
--- /dev/null
+++ b/account/src/test/java/com/ning/billing/account/dao/MockAccountDao.java
@@ -0,0 +1,98 @@
+/*
+ * 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.account.dao;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.UUID;
+import java.util.concurrent.ConcurrentHashMap;
+
+import com.google.inject.Inject;
+import com.ning.billing.account.api.Account;
+import com.ning.billing.account.api.AccountChangeNotification;
+import com.ning.billing.account.api.user.DefaultAccountChangeNotification;
+import com.ning.billing.account.api.user.DefaultAccountCreationEvent;
+import com.ning.billing.util.eventbus.Bus;
+import com.ning.billing.util.eventbus.Bus.EventBusException;
+
+public class MockAccountDao implements AccountDao {
+    private final Bus eventBus;
+    private final Map<String, Account> accounts = new ConcurrentHashMap<String, Account>();
+
+    @Inject
+    public MockAccountDao(Bus eventBus) {
+        this.eventBus = eventBus;
+    }
+
+    @Override
+    public void create(Account account) {
+        accounts.put(account.getId().toString(), account);
+
+        try {
+            eventBus.post(new DefaultAccountCreationEvent(account));
+        }
+        catch (EventBusException ex) {
+            throw new RuntimeException(ex);
+        }
+    }
+
+    @Override
+    public Account getById(String id) {
+        return accounts.get(id);
+    }
+
+    @Override
+    public List<Account> get() {
+        return new ArrayList<Account>(accounts.values());
+    }
+
+    @Override
+    public void test() {
+    }
+
+    @Override
+    public Account getAccountByKey(String key) {
+        for (Account account : accounts.values()) {
+            if (key.equals(account.getExternalKey())) {
+                return account;
+            }
+        }
+        return null;
+    }
+
+    @Override
+    public UUID getIdFromKey(String externalKey) {
+        Account account = getAccountByKey(externalKey);
+        return account == null ? null : account.getId();
+    }
+
+    @Override
+    public void update(Account account) {
+        Account currentAccount = accounts.put(account.getId().toString(), account);
+
+        AccountChangeNotification changeEvent = new DefaultAccountChangeNotification(account.getId(), currentAccount, account);
+        if (changeEvent.hasChanges()) {
+            try {
+                eventBus.post(changeEvent);
+            }
+            catch (EventBusException ex) {
+                throw new RuntimeException(ex);
+            }
+        }
+    }
+}
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 a4b0b98..c67b70e 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
@@ -16,26 +16,29 @@
 
 package com.ning.billing.account.dao;
 
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNotNull;
+import static org.testng.Assert.assertTrue;
+
 import java.util.List;
 import java.util.UUID;
+
 import org.joda.time.DateTime;
+import org.joda.time.DateTimeZone;
 import org.testng.annotations.Test;
+
 import com.ning.billing.account.api.Account;
 import com.ning.billing.account.api.AccountApiException;
 import com.ning.billing.account.api.AccountData;
 import com.ning.billing.account.api.DefaultAccount;
-import com.ning.billing.util.tag.DefaultTagDescription;
-import com.ning.billing.util.tag.Tag;
-import com.ning.billing.util.tag.TagDescription;
 import com.ning.billing.account.api.user.AccountBuilder;
 import com.ning.billing.catalog.api.Currency;
 import com.ning.billing.util.clock.DefaultClock;
+import com.ning.billing.util.tag.DefaultTagDescription;
+import com.ning.billing.util.tag.Tag;
+import com.ning.billing.util.tag.TagDescription;
 import com.ning.billing.util.tag.dao.TagDescriptionDao;
 
-import static org.testng.Assert.assertEquals;
-import static org.testng.Assert.assertNotNull;
-import static org.testng.Assert.assertTrue;
-
 @Test(groups = {"account-dao"})
 public class TestSimpleAccountDao extends AccountDaoTestBase {
     private final String key = "test1234";
@@ -48,10 +51,19 @@ public class TestSimpleAccountDao extends AccountDaoTestBase {
         String thisEmail = email + " " + UUID.randomUUID();
         String name = firstName + " " + lastName;
         String phone = "123-456-7890";
+        DateTime createdDate = new DateTime(DateTimeZone.UTC);
+        DateTime updatedDate = new DateTime(DateTimeZone.UTC);
 
         int firstNameLength = firstName.length();
-        return new AccountBuilder().externalKey(thisKey).name(name).phone(phone).firstNameLength(firstNameLength)
-                                   .email(thisEmail).currency(Currency.USD).build();
+        return new AccountBuilder().externalKey(thisKey)
+                                   .name(name)
+                                   .phone(phone)
+                                   .firstNameLength(firstNameLength)
+                                   .email(thisEmail)
+                                   .currency(Currency.USD)
+                                   .createdDate(createdDate)
+                                   .updatedDate(updatedDate)
+                                   .build();
     }
 
     public void testBasic() {
@@ -151,34 +163,42 @@ public class TestSimpleAccountDao extends AccountDaoTestBase {
             public String getExternalKey() {
                 return account.getExternalKey();
             }
+
             @Override
             public String getName() {
                 return "Jane Doe";
             }
+
             @Override
             public int getFirstNameLength() {
                 return 4;
             }
+
             @Override
             public String getEmail() {
                 return account.getEmail();
             }
+
             @Override
             public String getPhone() {
                 return account.getPhone();
             }
+
             @Override
             public int getBillCycleDay() {
                 return account.getBillCycleDay();
             }
+
             @Override
             public Currency getCurrency() {
                 return account.getCurrency();
             }
+
             @Override
             public String getPaymentProviderName() {
                 return account.getPaymentProviderName();
             }
+
         };
 
         Account updatedAccount = new DefaultAccount(account.getId(), accountData);
diff --git a/account/src/test/java/com/ning/billing/account/glue/AccountModuleWithMocks.java b/account/src/test/java/com/ning/billing/account/glue/AccountModuleWithMocks.java
new file mode 100644
index 0000000..fb72404
--- /dev/null
+++ b/account/src/test/java/com/ning/billing/account/glue/AccountModuleWithMocks.java
@@ -0,0 +1,28 @@
+/*
+ * 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.account.glue;
+
+import com.ning.billing.account.dao.AccountDao;
+import com.ning.billing.account.dao.MockAccountDao;
+
+public class AccountModuleWithMocks extends AccountModule {
+    @Override
+    protected void installAccountDao() {
+        bind(MockAccountDao.class).asEagerSingleton();
+        bind(AccountDao.class).to(MockAccountDao.class);
+    }
+}
diff --git a/analytics/pom.xml b/analytics/pom.xml
index 278348b..4cd7332 100644
--- a/analytics/pom.xml
+++ b/analytics/pom.xml
@@ -13,7 +13,7 @@
     <parent>
         <groupId>com.ning.billing</groupId>
         <artifactId>killbill</artifactId>
-        <version>0.1.2-SNAPSHOT</version>
+        <version>0.1.3-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>killbill-analytics</artifactId>
diff --git a/analytics/src/test/java/com/ning/billing/analytics/dao/MockBusinessAccountDao.java b/analytics/src/test/java/com/ning/billing/analytics/dao/MockBusinessAccountDao.java
new file mode 100644
index 0000000..f3dead0
--- /dev/null
+++ b/analytics/src/test/java/com/ning/billing/analytics/dao/MockBusinessAccountDao.java
@@ -0,0 +1,44 @@
+/*
+ * 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.analytics.dao;
+
+import com.ning.billing.analytics.BusinessAccount;
+
+public class MockBusinessAccountDao implements BusinessAccountDao {
+
+    @Override
+    public BusinessAccount getAccount(String key) {
+        // TODO Auto-generated method stub
+        return null;
+    }
+
+    @Override
+    public int createAccount(BusinessAccount account) {
+        // TODO Auto-generated method stub
+        return 0;
+    }
+
+    @Override
+    public int saveAccount(BusinessAccount account) {
+        // TODO Auto-generated method stub
+        return 0;
+    }
+
+    @Override
+    public void test() {
+    }
+}
diff --git a/analytics/src/test/java/com/ning/billing/analytics/MockAccount.java b/analytics/src/test/java/com/ning/billing/analytics/MockAccount.java
index 3b38c8c..6fdfe50 100644
--- a/analytics/src/test/java/com/ning/billing/analytics/MockAccount.java
+++ b/analytics/src/test/java/com/ning/billing/analytics/MockAccount.java
@@ -16,14 +16,16 @@
 
 package com.ning.billing.analytics;
 
-import sun.reflect.generics.reflectiveObjects.NotImplementedException;
-
 import java.math.BigDecimal;
 import java.util.List;
 import java.util.UUID;
+
 import org.joda.time.DateTime;
+import org.joda.time.DateTimeZone;
+
+import sun.reflect.generics.reflectiveObjects.NotImplementedException;
+
 import com.ning.billing.account.api.Account;
-import com.ning.billing.account.api.AccountData;
 import com.ning.billing.catalog.api.Currency;
 import com.ning.billing.util.customfield.CustomField;
 import com.ning.billing.util.tag.Tag;
@@ -167,4 +169,14 @@ public class MockAccount implements Account
     public BigDecimal getBalance() {
         return BigDecimal.ZERO;
     }
+
+    @Override
+    public DateTime getCreatedDate() {
+        return new DateTime(DateTimeZone.UTC);
+    }
+
+    @Override
+    public DateTime getUpdatedDate() {
+        return new DateTime(DateTimeZone.UTC);
+    }
 }

api/pom.xml 10(+9 -1)

diff --git a/api/pom.xml b/api/pom.xml
index dce65e7..89f3a43 100644
--- a/api/pom.xml
+++ b/api/pom.xml
@@ -13,7 +13,7 @@
     <parent>
         <groupId>com.ning.billing</groupId>
         <artifactId>killbill</artifactId>
-        <version>0.1.2-SNAPSHOT</version>
+        <version>0.1.3-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>killbill-api</artifactId>
@@ -50,6 +50,14 @@
             <groupId>org.skife.config</groupId>
             <artifactId>config-magic</artifactId>
         </dependency>
+        <dependency>
+            <groupId>org.codehaus.jackson</groupId>
+            <artifactId>jackson-core-asl</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>commons-lang</groupId>
+            <artifactId>commons-lang</artifactId>
+        </dependency>
 
     </dependencies>
     <build>
diff --git a/api/src/main/java/com/ning/billing/account/api/Account.java b/api/src/main/java/com/ning/billing/account/api/Account.java
index 543f280..5272630 100644
--- a/api/src/main/java/com/ning/billing/account/api/Account.java
+++ b/api/src/main/java/com/ning/billing/account/api/Account.java
@@ -17,9 +17,17 @@
 package com.ning.billing.account.api;
 
 import java.math.BigDecimal;
+
+import org.joda.time.DateTime;
+
 import com.ning.billing.util.customfield.CustomizableEntity;
 import com.ning.billing.util.tag.Taggable;
 
 public interface Account extends AccountData, CustomizableEntity, Taggable {
     public BigDecimal getBalance();
+
+    public DateTime getCreatedDate();
+
+    public DateTime getUpdatedDate();
+
 }
diff --git a/api/src/main/java/com/ning/billing/account/api/AccountData.java b/api/src/main/java/com/ning/billing/account/api/AccountData.java
index 9b8f399..e395706 100644
--- a/api/src/main/java/com/ning/billing/account/api/AccountData.java
+++ b/api/src/main/java/com/ning/billing/account/api/AccountData.java
@@ -17,7 +17,6 @@
 package com.ning.billing.account.api;
 
 import com.ning.billing.catalog.api.Currency;
-import com.ning.billing.util.entity.Entity;
 
 public interface AccountData {
 
@@ -36,4 +35,5 @@ public interface AccountData {
     public Currency getCurrency();
 
     public String getPaymentProviderName();
+
 }
diff --git a/api/src/main/java/com/ning/billing/invoice/api/InvoiceCreationNotification.java b/api/src/main/java/com/ning/billing/invoice/api/InvoiceCreationNotification.java
index 7903647..86f2f23 100644
--- a/api/src/main/java/com/ning/billing/invoice/api/InvoiceCreationNotification.java
+++ b/api/src/main/java/com/ning/billing/invoice/api/InvoiceCreationNotification.java
@@ -16,17 +16,19 @@
 
 package com.ning.billing.invoice.api;
 
-import com.ning.billing.catalog.api.Currency;
-import com.ning.billing.util.eventbus.BusEvent;
-import org.joda.time.DateTime;
-
 import java.math.BigDecimal;
 import java.util.UUID;
 
+import org.joda.time.DateTime;
+
+import com.ning.billing.catalog.api.Currency;
+import com.ning.billing.util.eventbus.BusEvent;
+
 public interface InvoiceCreationNotification extends BusEvent {
     public UUID getInvoiceId();
     public UUID getAccountId();
     public BigDecimal getAmountOwed();
     public Currency getCurrency();
     public DateTime getInvoiceCreationDate();
+
 }
\ No newline at end of file
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 a4acadb..b947762 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
@@ -22,12 +22,18 @@ import org.joda.time.DateTime;
 import com.ning.billing.catalog.api.Currency;
 import com.ning.billing.util.entity.Entity;
 
-public interface InvoicePayment extends Entity {
+public interface InvoicePayment {
+    UUID getPaymentAttemptId();
+
     UUID getInvoiceId();
 
-    DateTime getPaymentDate();
+    DateTime getPaymentAttemptDate();
 
     BigDecimal getAmount();
 
     Currency getCurrency();
+
+    DateTime getCreatedDate();
+
+    DateTime getUpdatedDate();
 }
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 bde12a9..641afa6 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
@@ -16,19 +16,28 @@
 
 package com.ning.billing.invoice.api;
 
-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, DateTime paymentAttemptDate);
+import com.ning.billing.catalog.api.Currency;
 
-    public void paymentFailed(UUID invoiceId, UUID paymentId, DateTime paymentAttemptDate);
+public interface InvoicePaymentApi {
 
     public List<Invoice> getInvoicesByAccount(UUID accountId);
 
     public Invoice getInvoice(UUID invoiceId);
+
+    public Invoice getInvoiceForPaymentAttemptId(UUID paymentAttemptId);
+
+    public InvoicePayment getInvoicePayment(UUID paymentAttemptId);
+
+    public void notifyOfPaymentAttempt(InvoicePayment invoicePayment);
+
+    public void notifyOfPaymentAttempt(UUID invoiceId, BigDecimal amountOutstanding, Currency currency, UUID paymentAttemptId, DateTime paymentAttemptDate);
+
+    public void notifyOfPaymentAttempt(UUID invoiceId, UUID paymentAttemptId, DateTime paymentAttemptDate);
+
 }
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 c9169c4..363e483 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
@@ -22,7 +22,6 @@ import java.math.BigDecimal;
 import java.util.Collection;
 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);
@@ -37,10 +36,7 @@ public interface InvoiceUserApi {
 
     public Invoice getInvoice(UUID invoiceId);
 
-    public void paymentAttemptFailed(UUID invoiceId, UUID paymentId, DateTime paymentAttemptDate);
-
-    public void paymentAttemptSuccessful(UUID invoiceId, BigDecimal amount, Currency currency,
-                                         UUID paymentId, DateTime paymentDate);
+    public void notifyOfPaymentAttempt(InvoicePayment invoicePayment);
 
     public Collection<Invoice> getUnpaidInvoicesByAccountId(UUID accountId, DateTime upToDate);
 }
diff --git a/api/src/main/java/com/ning/billing/payment/api/CreditCardPaymentMethodInfo.java b/api/src/main/java/com/ning/billing/payment/api/CreditCardPaymentMethodInfo.java
new file mode 100644
index 0000000..75a4ab2
--- /dev/null
+++ b/api/src/main/java/com/ning/billing/payment/api/CreditCardPaymentMethodInfo.java
@@ -0,0 +1,94 @@
+/*
+ * 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.payment.api;
+
+
+public final class CreditCardPaymentMethodInfo extends PaymentMethodInfo {
+    public static final class Builder extends BuilderBase<CreditCardPaymentMethodInfo, Builder> {
+        private String cardHolderName;
+        private String cardType;
+        private String expirationDate;
+        private String maskNumber;
+
+        public Builder() {
+            super(Builder.class);
+        }
+
+        public Builder(CreditCardPaymentMethodInfo src) {
+            super(Builder.class, src);
+        }
+
+        public Builder setCardHolderName(String cardHolderName) {
+            this.cardHolderName = cardHolderName;
+            return this;
+        }
+
+        public Builder setCardType(String cardType) {
+            this.cardType = cardType;
+            return this;
+        }
+
+        public Builder setExpirationDateStr(String expirationDateStr) {
+            this.expirationDate = expirationDateStr;
+            return this;
+        }
+
+        public Builder setMaskNumber(String maskNumber) {
+            this.maskNumber = maskNumber;
+            return this;
+        }
+
+        public CreditCardPaymentMethodInfo build() {
+            return new CreditCardPaymentMethodInfo(id, accountId, defaultMethod, cardHolderName, cardType, expirationDate, maskNumber);
+        }
+    }
+
+    private final String cardHolderName;
+    private final String cardType;
+    private final String expirationDate;
+    private final String maskNumber;
+
+    public CreditCardPaymentMethodInfo(String id,
+                                   String accountId,
+                                   Boolean defaultMethod,
+                                   String cardHolderName,
+                                   String cardType,
+                                   String expirationDate,
+                                   String maskNumber) {
+      super(id, accountId, defaultMethod, "CreditCard");
+      this.cardHolderName = cardHolderName;
+      this.cardType = cardType;
+      this.expirationDate = expirationDate;
+      this.maskNumber = maskNumber;
+    }
+
+    public String getCardHolderName() {
+      return cardHolderName;
+    }
+
+    public String getCardType() {
+      return cardType;
+    }
+
+    public String getExpirationDate() {
+      return expirationDate;
+    }
+
+    public String getMaskNumber() {
+      return maskNumber;
+    }
+}
diff --git a/api/src/main/java/com/ning/billing/payment/api/Either.java b/api/src/main/java/com/ning/billing/payment/api/Either.java
new file mode 100644
index 0000000..25ce8f8
--- /dev/null
+++ b/api/src/main/java/com/ning/billing/payment/api/Either.java
@@ -0,0 +1,79 @@
+/*
+ * 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.payment.api;
+
+import org.codehaus.jackson.annotate.JsonValue;
+
+public abstract class Either<T, V> {
+    public static <T, V> Either<T, V> left(T value) {
+        return new Left<T, V>(value);
+    }
+    public static <T, V> Either<T, V> right(V value) {
+        return new Right<T, V>(value);
+    }
+
+    private Either() {
+    }
+
+    public boolean isLeft() {
+        return false;
+    }
+    public boolean isRight() {
+        return false;
+    }
+    public T getLeft() {
+        throw new UnsupportedOperationException();
+    }
+    public V getRight() {
+        throw new UnsupportedOperationException();
+    }
+
+    public static class Left<T, V> extends Either<T, V> {
+        private final T value;
+
+        public Left(T value) {
+            this.value = value;
+        }
+        @Override
+        public boolean isLeft() {
+            return true;
+        }
+        @Override
+        @JsonValue
+        public T getLeft() {
+            return value;
+        }
+    }
+
+    public static class Right<T, V> extends Either<T, V> {
+        private final V value;
+
+        public Right(V value) {
+            this.value = value;
+        }
+        @Override
+        public boolean isRight() {
+            return true;
+        }
+
+        @Override
+        @JsonValue
+        public V getRight() {
+            return value;
+        }
+    }
+}
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
new file mode 100644
index 0000000..1d62eb5
--- /dev/null
+++ b/api/src/main/java/com/ning/billing/payment/api/PaymentApi.java
@@ -0,0 +1,51 @@
+/*
+ * 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.payment.api;
+
+import java.util.List;
+
+import javax.annotation.Nullable;
+
+import com.ning.billing.account.api.Account;
+
+public interface PaymentApi {
+    Either<PaymentError, PaymentMethodInfo> getPaymentMethod(@Nullable String accountKey, String paymentMethodId);
+
+    Either<PaymentError, List<PaymentMethodInfo>> getPaymentMethods(String accountKey);
+
+    Either<PaymentError, Void> deletePaymentMethod(String accountKey, String paymentMethodId);
+
+    Either<PaymentError, Void> updatePaymentGateway(String accountKey);
+
+    Either<PaymentError, String> addPaypalPaymentMethod(@Nullable String accountKey, PaypalPaymentMethodInfo paypalPaymentMethod);
+
+    Either<PaymentError, PaymentMethodInfo> updatePaymentMethod(String accountKey, PaymentMethodInfo paymentMethodInfo);
+
+    List<Either<PaymentError, PaymentInfo>> createPayment(String accountKey, List<String> invoiceIds);
+    List<Either<PaymentError, PaymentInfo>> createPayment(Account account, List<String> invoiceIds);
+
+    List<Either<PaymentError, PaymentInfo>> createRefund(Account account, List<String> invoiceIds); //TODO
+
+    Either<PaymentError, PaymentProviderAccount> getPaymentProviderAccount(String accountKey);
+
+    Either<PaymentError, PaymentProviderAccount> createPaymentProviderAccount(PaymentProviderAccount account);
+
+    Either<PaymentError, PaymentProviderAccount> updatePaymentProviderAccount(PaymentProviderAccount account);
+
+    PaymentAttempt getPaymentAttemptForPaymentId(String id);
+
+}
diff --git a/api/src/main/java/com/ning/billing/payment/api/PaymentAttempt.java b/api/src/main/java/com/ning/billing/payment/api/PaymentAttempt.java
new file mode 100644
index 0000000..48d89e9
--- /dev/null
+++ b/api/src/main/java/com/ning/billing/payment/api/PaymentAttempt.java
@@ -0,0 +1,260 @@
+/*
+ * 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.payment.api;
+
+import java.math.BigDecimal;
+import java.util.UUID;
+
+import org.joda.time.DateTime;
+import org.joda.time.DateTimeZone;
+
+import com.google.common.base.Objects;
+import com.ning.billing.catalog.api.Currency;
+import com.ning.billing.invoice.api.Invoice;
+
+public class PaymentAttempt {
+    private final UUID paymentAttemptId;
+    private final UUID invoiceId;
+    private final UUID accountId;
+    private final BigDecimal amount;
+    private final Currency currency;
+    private final String paymentId;
+    private final DateTime invoiceDate;
+    private final DateTime paymentAttemptDate;
+    private final DateTime createdDate;
+    private final DateTime updatedDate;
+
+    public PaymentAttempt(UUID paymentAttemptId,
+                          UUID invoiceId,
+                          UUID accountId,
+                          BigDecimal amount,
+                          Currency currency,
+                          DateTime invoiceDate,
+                          DateTime paymentAttemptDate,
+                          String paymentId,
+                          DateTime createdDate,
+                          DateTime updatedDate) {
+        this.paymentAttemptId = paymentAttemptId;
+        this.invoiceId = invoiceId;
+        this.accountId = accountId;
+        this.amount = amount;
+        this.currency = currency;
+        this.invoiceDate = invoiceDate;
+        this.paymentAttemptDate = paymentAttemptDate == null ? new DateTime(DateTimeZone.UTC) : paymentAttemptDate;
+        this.paymentId = paymentId;
+        this.createdDate = createdDate;
+        this.updatedDate = updatedDate;
+    }
+
+    public PaymentAttempt(UUID paymentAttemptId,
+                          UUID invoiceId,
+                          UUID accountId,
+                          BigDecimal amount,
+                          Currency currency,
+                          DateTime invoiceDate,
+                          DateTime paymentAttemptDate,
+                          String paymentId) {
+        this(paymentAttemptId, invoiceId, accountId, amount, currency, invoiceDate, paymentAttemptDate, paymentId, new DateTime(DateTimeZone.UTC), new DateTime(DateTimeZone.UTC));
+    }
+
+    public PaymentAttempt(UUID paymentAttemptId, UUID invoiceId, UUID accountId, BigDecimal amount, Currency currency, DateTime invoiceDate, DateTime paymentAttemptDate) {
+        this(paymentAttemptId, invoiceId, accountId, amount, currency, invoiceDate, paymentAttemptDate, null);
+    }
+
+    public PaymentAttempt(UUID paymentAttemptId, UUID invoiceId, UUID accountId, DateTime invoiceDate, DateTime paymentAttemptDate) {
+        this(paymentAttemptId, invoiceId, accountId, null, null, invoiceDate, paymentAttemptDate, null);
+    }
+
+    public PaymentAttempt(UUID paymentAttemptId, Invoice invoice) {
+        this(paymentAttemptId, invoice.getId(), invoice.getAccountId(), invoice.getBalance(), invoice.getCurrency(), invoice.getInvoiceDate(), null);
+    }
+
+    public DateTime getInvoiceDate() {
+        return invoiceDate;
+    }
+
+    public UUID getPaymentAttemptId() {
+        return paymentAttemptId;
+    }
+
+    public String getPaymentId() {
+        return paymentId;
+    }
+
+    public DateTime getPaymentAttemptDate() {
+        return paymentAttemptDate;
+    }
+
+    public UUID getInvoiceId() {
+        return invoiceId;
+    }
+
+    public UUID getAccountId() {
+        return accountId;
+    }
+
+    public DateTime getCreatedDate() {
+        return createdDate;
+    }
+
+    public DateTime getUpdatedDate() {
+        return updatedDate;
+    }
+
+    public BigDecimal getAmount() {
+        return amount;
+    }
+
+    public Currency getCurrency() {
+        return currency;
+    }
+
+    @Override
+    public String toString() {
+        return "PaymentAttempt [paymentAttemptId=" + paymentAttemptId + ", invoiceId=" + invoiceId + ", amount=" + amount + ", currency=" + currency + ", paymentId=" + paymentId + ", paymentAttemptDate=" + paymentAttemptDate + "]";
+    }
+
+    public Builder cloner() {
+        return new Builder(this);
+    }
+
+    public static class Builder {
+        private UUID paymentAttemptId;
+        private UUID invoiceId;
+        private UUID accountId;
+        private BigDecimal amount;
+        private Currency currency;
+        private DateTime invoiceDate;
+        private DateTime paymentAttemptDate;
+        private String paymentId;
+        private DateTime createdDate;
+        private DateTime updatedDate;
+
+        public Builder() {
+        }
+
+        public Builder(PaymentAttempt src) {
+            this.paymentAttemptId = src.paymentAttemptId;
+            this.invoiceId = src.invoiceId;
+            this.accountId = src.accountId;
+            this.amount = src.amount;
+            this.currency = src.currency;
+            this.invoiceDate = src.invoiceDate;
+            this.paymentAttemptDate = src.paymentAttemptDate;
+            this.paymentId = src.paymentId;
+            this.createdDate = src.createdDate;
+            this.updatedDate = src.updatedDate;
+        }
+
+        public Builder setPaymentAttemptId(UUID paymentAttemptId) {
+            this.paymentAttemptId = paymentAttemptId;
+            return this;
+        }
+
+        public Builder setInvoiceId(UUID invoiceId) {
+            this.invoiceId = invoiceId;
+            return this;
+        }
+
+        public Builder setAccountId(UUID accountId) {
+            this.accountId = accountId;
+            return this;
+        }
+
+        public Builder setAmount(BigDecimal amount) {
+            this.amount = amount;
+            return this;
+        }
+
+        public Builder setCurrency(Currency currency) {
+            this.currency = currency;
+            return this;
+        }
+
+        public Builder setCreatedDate(DateTime createdDate) {
+            this.createdDate = createdDate;
+            return this;
+        }
+
+        public Builder setUpdatedDate(DateTime updatedDate) {
+            this.updatedDate = updatedDate;
+            return this;
+        }
+
+        public Builder setInvoiceDate(DateTime invoiceDate) {
+            this.invoiceDate = invoiceDate;
+            return this;
+        }
+
+        public Builder setPaymentAttemptDate(DateTime paymentAttemptDate) {
+            this.paymentAttemptDate = paymentAttemptDate;
+            return this;
+        }
+
+        public Builder setPaymentId(String paymentId) {
+            this.paymentId = paymentId;
+            return this;
+        }
+
+        public PaymentAttempt build() {
+            return new PaymentAttempt(paymentAttemptId,
+                                      invoiceId,
+                                      accountId,
+                                      amount,
+                                      currency,
+                                      invoiceDate,
+                                      paymentAttemptDate,
+                                      paymentId,
+                                      createdDate,
+                                      updatedDate);
+        }
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hashCode(paymentAttemptId,
+                                invoiceId,
+                                accountId,
+                                amount,
+                                currency,
+                                invoiceDate,
+                                paymentAttemptDate,
+                                paymentId);
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (getClass() == obj.getClass()) {
+            PaymentAttempt other = (PaymentAttempt)obj;
+            if (obj == other) {
+                return true;
+            }
+            else {
+                return Objects.equal(paymentAttemptId, other.paymentAttemptId) &&
+                       Objects.equal(invoiceId, other.invoiceId) &&
+                       Objects.equal(accountId, other.accountId) &&
+                       Objects.equal(amount, other.amount) &&
+                       Objects.equal(currency, other.currency) &&
+                       Objects.equal(invoiceDate, other.invoiceDate) &&
+                       Objects.equal(paymentAttemptDate, other.paymentAttemptDate) &&
+                       Objects.equal(paymentId, other.paymentId);
+            }
+        }
+        return false;
+    }
+
+}
diff --git a/api/src/main/java/com/ning/billing/payment/api/PaymentError.java b/api/src/main/java/com/ning/billing/payment/api/PaymentError.java
new file mode 100644
index 0000000..2eba03d
--- /dev/null
+++ b/api/src/main/java/com/ning/billing/payment/api/PaymentError.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.payment.api;
+import org.codehaus.jackson.annotate.JsonTypeInfo;
+import org.codehaus.jackson.annotate.JsonTypeInfo.Id;
+
+import com.ning.billing.util.eventbus.BusEvent;
+
+@JsonTypeInfo(use = Id.NAME, property = "error")
+public class PaymentError implements BusEvent {
+    private final String type;
+    private final String message;
+
+    public PaymentError(PaymentError src) {
+        this.type = src.type;
+        this.message = src.message;
+    }
+
+    public PaymentError(String type, String message) {
+        this.type = type;
+        this.message = message;
+    }
+
+    public String getType() {
+        return type;
+    }
+
+    public String getMessage() {
+        return message;
+    }
+
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result + ((message == null) ? 0 : message.hashCode());
+        result = prime * result + ((type == null) ? 0 : type.hashCode());
+        return result;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj)
+            return true;
+        if (obj == null)
+            return false;
+        if (getClass() != obj.getClass())
+            return false;
+        PaymentError other = (PaymentError) obj;
+        if (message == null) {
+            if (other.message != null)
+                return false;
+        }
+        else if (!message.equals(other.message))
+            return false;
+        if (type == null) {
+            if (other.type != null)
+                return false;
+        }
+        else if (!type.equals(other.type))
+            return false;
+        return true;
+    }
+
+    @Override
+    public String toString() {
+        return "PaymentError [type=" + type + ", message=" + message + "]";
+    }
+}
diff --git a/api/src/main/java/com/ning/billing/payment/api/PaymentInfo.java b/api/src/main/java/com/ning/billing/payment/api/PaymentInfo.java
new file mode 100644
index 0000000..6fa5c60
--- /dev/null
+++ b/api/src/main/java/com/ning/billing/payment/api/PaymentInfo.java
@@ -0,0 +1,271 @@
+/*
+ * 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.payment.api;
+
+import java.math.BigDecimal;
+
+import org.codehaus.jackson.annotate.JsonCreator;
+import org.codehaus.jackson.annotate.JsonProperty;
+import org.joda.time.DateTime;
+
+import com.google.common.base.Objects;
+import com.ning.billing.util.eventbus.BusEvent;
+
+public class PaymentInfo implements BusEvent {
+    private final String paymentId;
+    private final BigDecimal amount;
+    private final BigDecimal refundAmount;
+    private final String paymentNumber;
+    private final String bankIdentificationNumber;
+    private final String status;
+    private final String type;
+    private final String referenceId;
+    private final DateTime effectiveDate;
+    private final DateTime createdDate;
+    private final DateTime updatedDate;
+
+    public PaymentInfo(PaymentInfo src) {
+        this.paymentId = src.paymentId;
+        this.amount = src.amount;
+        this.refundAmount = src.refundAmount;
+        this.paymentNumber = src.paymentNumber;
+        this.bankIdentificationNumber = src.bankIdentificationNumber;
+        this.status = src.status;
+        this.type = src.type;
+        this.referenceId = src.referenceId;
+        this.effectiveDate = src.effectiveDate;
+        this.createdDate = src.createdDate;
+        this.updatedDate = src.updatedDate;
+    }
+
+    @JsonCreator
+    public PaymentInfo(@JsonProperty("paymentId") String paymentId,
+                       @JsonProperty("amount") BigDecimal amount,
+                       @JsonProperty("refundAmount") BigDecimal refundAmount,
+                       @JsonProperty("bankIdentificationNumber") String bankIdentificationNumber,
+                       @JsonProperty("paymentNumber") String paymentNumber,
+                       @JsonProperty("status") String status,
+                       @JsonProperty("type") String type,
+                       @JsonProperty("referenceId") String referenceId,
+                       @JsonProperty("effectiveDate") DateTime effectiveDate,
+                       @JsonProperty("createdDate") DateTime createdDate,
+                       @JsonProperty("updatedDate") DateTime updatedDate) {
+        this.paymentId = paymentId;
+        this.amount = amount;
+        this.refundAmount = refundAmount;
+        this.bankIdentificationNumber = bankIdentificationNumber;
+        this.effectiveDate = effectiveDate;
+        this.paymentNumber = paymentNumber;
+        this.referenceId = referenceId;
+        this.status = status;
+        this.type = type;
+        this.createdDate = createdDate;
+        this.updatedDate = updatedDate;
+    }
+
+    public Builder cloner() {
+        return new Builder(this);
+    }
+
+    public String getPaymentId() {
+        return paymentId;
+    }
+
+    public BigDecimal getAmount() {
+        return amount;
+    }
+
+    public String getBankIdentificationNumber() {
+        return bankIdentificationNumber;
+    }
+
+    public DateTime getCreatedDate() {
+        return createdDate;
+    }
+
+    public DateTime getEffectiveDate() {
+        return effectiveDate;
+    }
+
+    public String getPaymentNumber() {
+        return paymentNumber;
+    }
+
+    public String getReferenceId() {
+        return referenceId;
+    }
+
+    public BigDecimal getRefundAmount() {
+        return refundAmount;
+    }
+
+    public String getStatus() {
+        return status;
+    }
+
+    public String getType() {
+        return type;
+    }
+
+    public DateTime getUpdatedDate() {
+        return updatedDate;
+    }
+
+    public static class Builder {
+        private String paymentId;
+        private BigDecimal amount;
+        private BigDecimal refundAmount;
+        private String paymentNumber;
+        private String bankIdentificationNumber;
+        private String type;
+        private String status;
+        private String referenceId;
+        private DateTime effectiveDate;
+        private DateTime createdDate;
+        private DateTime updatedDate;
+
+        public Builder() {
+        }
+
+        public Builder(PaymentInfo src) {
+            this.paymentId = src.paymentId;
+            this.amount = src.amount;
+            this.refundAmount = src.refundAmount;
+            this.paymentNumber = src.paymentNumber;
+            this.bankIdentificationNumber = src.bankIdentificationNumber;
+            this.type = src.type;
+            this.status = src.status;
+            this.effectiveDate = src.effectiveDate;
+            this.referenceId = src.referenceId;
+            this.createdDate = src.createdDate;
+            this.updatedDate = src.updatedDate;
+        }
+
+        public Builder setPaymentId(String paymentId) {
+            this.paymentId = paymentId;
+            return this;
+        }
+
+        public Builder setAmount(BigDecimal amount) {
+            this.amount = amount;
+            return this;
+        }
+
+        public Builder setBankIdentificationNumber(String bankIdentificationNumber) {
+            this.bankIdentificationNumber = bankIdentificationNumber;
+            return this;
+        }
+
+        public Builder setCreatedDate(DateTime createdDate) {
+            this.createdDate = createdDate;
+            return this;
+        }
+
+        public Builder setEffectiveDate(DateTime effectiveDate) {
+            this.effectiveDate = effectiveDate;
+            return this;
+        }
+
+        public Builder setPaymentNumber(String paymentNumber) {
+            this.paymentNumber = paymentNumber;
+            return this;
+        }
+
+        public Builder setReferenceId(String referenceId) {
+            this.referenceId = referenceId;
+            return this;
+        }
+
+        public Builder setRefundAmount(BigDecimal refundAmount) {
+            this.refundAmount = refundAmount;
+            return this;
+        }
+
+        public Builder setStatus(String status) {
+            this.status = status;
+            return this;
+        }
+
+        public Builder setType(String type) {
+            this.type = type;
+            return this;
+        }
+
+        public Builder setUpdatedDate(DateTime updatedDate) {
+            this.updatedDate = updatedDate;
+            return this;
+        }
+
+        public PaymentInfo build() {
+            return new PaymentInfo(paymentId,
+                                   amount,
+                                   refundAmount,
+                                   bankIdentificationNumber,
+                                   paymentNumber,
+                                   type,
+                                   status,
+                                   referenceId,
+                                   effectiveDate,
+                                   createdDate,
+                                   updatedDate);
+        }
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hashCode(amount,
+                                bankIdentificationNumber,
+                                createdDate,
+                                effectiveDate,
+                                paymentId,
+                                paymentNumber,
+                                referenceId,
+                                refundAmount,
+                                status,
+                                type,
+                                updatedDate);
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (getClass() == obj.getClass()) {
+            PaymentInfo other = (PaymentInfo)obj;
+            if (obj == other) {
+                return true;
+            }
+            else {
+                return Objects.equal(amount, other.amount) &&
+                       Objects.equal(bankIdentificationNumber, other.bankIdentificationNumber) &&
+                       Objects.equal(createdDate, other.createdDate) &&
+                       Objects.equal(effectiveDate, other.effectiveDate) &&
+                       Objects.equal(paymentId, other.paymentId) &&
+                       Objects.equal(paymentNumber, other.paymentNumber) &&
+                       Objects.equal(referenceId, other.referenceId) &&
+                       Objects.equal(refundAmount, other.refundAmount) &&
+                       Objects.equal(status, other.status) &&
+                       Objects.equal(type, other.type) &&
+                       Objects.equal(updatedDate, other.updatedDate);
+            }
+        }
+        return false;
+    }
+
+    @Override
+    public String toString() {
+        return "PaymentInfo [paymentId=" + paymentId + ", amount=" + amount + ", refundAmount=" + refundAmount + ", paymentNumber=" + paymentNumber + ", bankIdentificationNumber=" + bankIdentificationNumber + ", status=" + status + ", type=" + type + ", referenceId=" + referenceId + ", effectiveDate=" + effectiveDate + ", createdDate=" + createdDate + ", updatedDate=" + updatedDate + "]";
+    }
+}
diff --git a/api/src/main/java/com/ning/billing/payment/api/PaymentMethodInfo.java b/api/src/main/java/com/ning/billing/payment/api/PaymentMethodInfo.java
new file mode 100644
index 0000000..78f73d3
--- /dev/null
+++ b/api/src/main/java/com/ning/billing/payment/api/PaymentMethodInfo.java
@@ -0,0 +1,137 @@
+/*
+ * 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.payment.api;
+
+import com.google.common.base.Objects;
+
+public class PaymentMethodInfo {
+    private final String id;
+    private final String accountId;
+    private final Boolean defaultMethod;
+    private final String type;
+
+    public PaymentMethodInfo(String id,
+                             String accountId,
+                             Boolean defaultMethod,
+                             String type) {
+        this.id = id;
+        this.accountId = accountId;
+        this.defaultMethod = defaultMethod;
+        this.type = type;
+    }
+
+    public String getId() {
+        return id;
+    }
+
+    public String getAccountId() {
+        return accountId;
+    }
+
+    public Boolean getDefaultMethod() {
+        return defaultMethod;
+    }
+
+    public String getType() {
+        return type;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hashCode(id,
+                                accountId,
+                                defaultMethod,
+                                type);
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (getClass() == obj.getClass()) {
+            PaymentMethodInfo other = (PaymentMethodInfo)obj;
+            if (obj == other) {
+                return true;
+            }
+            else {
+                return Objects.equal(id, other.id) &&
+                       Objects.equal(accountId, other.accountId) &&
+                       Objects.equal(defaultMethod, other.defaultMethod) &&
+                       Objects.equal(type, other.type);
+            }
+        }
+        return false;
+    }
+
+    @Override
+    public String toString() {
+        return "PaymentMethodInfo [id=" + id + ", accountId=" + accountId + ", defaultMethod=" + defaultMethod + ", type=" + type + "]";
+    }
+
+    protected abstract static class BuilderBase<T extends PaymentMethodInfo, V extends BuilderBase<T, V>> {
+        protected final Class<V> builderClazz;
+        protected String id;
+        protected String accountId;
+        protected Boolean defaultMethod;
+
+        protected BuilderBase(Class<V> builderClazz) {
+            this.builderClazz = builderClazz;
+        }
+
+        protected BuilderBase(Class<V> builderClazz, T src) {
+            this(builderClazz);
+            this.id = src.id;
+            this.accountId = src.accountId;
+            this.defaultMethod = src.defaultMethod;
+        }
+
+        public V setId(String id) {
+            this.id = id;
+            return builderClazz.cast(this);
+        }
+
+        public V setAccountId(String accountId) {
+            this.accountId = accountId;
+            return builderClazz.cast(this);
+        }
+
+        public V setDefaultMethod(Boolean defaultMethod) {
+            this.defaultMethod = defaultMethod;
+            return builderClazz.cast(this);
+        }
+    }
+
+    public static class Builder extends BuilderBase<PaymentMethodInfo, Builder> {
+        private String type;
+
+        public Builder() {
+            super(Builder.class);
+        }
+
+        public Builder(PaymentMethodInfo src) {
+            super(Builder.class, src);
+            this.type = src.type;
+        }
+
+        public Builder setType(String type) {
+            this.type = type;
+            return this;
+        }
+
+        public PaymentMethodInfo build() {
+            return new PaymentMethodInfo(id, accountId, defaultMethod, type);
+        }
+    }
+}
diff --git a/api/src/main/java/com/ning/billing/payment/api/PaymentProviderAccount.java b/api/src/main/java/com/ning/billing/payment/api/PaymentProviderAccount.java
new file mode 100644
index 0000000..553d6a2
--- /dev/null
+++ b/api/src/main/java/com/ning/billing/payment/api/PaymentProviderAccount.java
@@ -0,0 +1,124 @@
+/*
+ * 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.payment.api;
+
+import com.google.common.base.Objects;
+
+public class PaymentProviderAccount {
+    private final String id;
+    private final String accountNumber;
+    private final String accountName;
+    private final String phoneNumber;
+    private final String defaultPaymentMethodId;
+
+    public PaymentProviderAccount(String id,
+                                  String accountNumber,
+                                  String accountName,
+                                  String phoneNumber,
+                                  String defaultPaymentMethodId) {
+        this.id = id;
+        this.accountNumber = accountNumber;
+        this.accountName = accountName;
+        this.phoneNumber = phoneNumber;
+        this.defaultPaymentMethodId = defaultPaymentMethodId;
+    }
+
+    public String getId() {
+        return id;
+    }
+
+    public String getAccountNumber() {
+        return accountNumber;
+    }
+
+    public String getAccountName() {
+        return accountName;
+    }
+
+    public String getPhoneNumber() {
+        return phoneNumber;
+    }
+
+    public String getDefaultPaymentMethodId() {
+        return defaultPaymentMethodId;
+    }
+
+    public static class Builder {
+        private String id;
+        private String accountNumber;
+        private String accountName;
+        private String phoneNumber;
+        private String defaultPaymentMethodId;
+
+        public Builder setId(String id) {
+            this.id = id;
+            return this;
+        }
+
+        public Builder setAccountNumber(String accountNumber) {
+            this.accountNumber = accountNumber;
+            return this;
+        }
+
+        public Builder setAccountName(String accountName) {
+            this.accountName = accountName;
+            return this;
+        }
+
+        public Builder setPhoneNumber(String phoneNumber) {
+            this.phoneNumber = phoneNumber;
+            return this;
+        }
+
+        public Builder setDefaultPaymentMethod(String defaultPaymentMethod) {
+            this.defaultPaymentMethodId = defaultPaymentMethod;
+            return this;
+        }
+
+        public PaymentProviderAccount build() {
+            return new PaymentProviderAccount(id, accountNumber, accountName, phoneNumber, defaultPaymentMethodId);
+        }
+
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hashCode(id,
+                                accountNumber,
+                                accountName,
+                                phoneNumber,
+                                defaultPaymentMethodId);
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (getClass() == obj.getClass()) {
+            PaymentProviderAccount other = (PaymentProviderAccount)obj;
+            if (obj == other) {
+                return true;
+            }
+            else {
+                return Objects.equal(id, other.id) &&
+                       Objects.equal(accountNumber, other.accountNumber) &&
+                       Objects.equal(phoneNumber, other.phoneNumber) &&
+                       Objects.equal(defaultPaymentMethodId, other.defaultPaymentMethodId);
+            }
+        }
+        return false;
+    }
+
+}
diff --git a/api/src/main/java/com/ning/billing/payment/api/PaymentProviderContactData.java b/api/src/main/java/com/ning/billing/payment/api/PaymentProviderContactData.java
new file mode 100644
index 0000000..7be9908
--- /dev/null
+++ b/api/src/main/java/com/ning/billing/payment/api/PaymentProviderContactData.java
@@ -0,0 +1,157 @@
+/*
+ * 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.payment.api;
+
+import org.apache.commons.lang.StringUtils;
+
+import com.google.common.base.Objects;
+import com.ning.billing.catalog.api.Currency;
+
+public class PaymentProviderContactData {
+    private final String firstName;
+    private final String lastName;
+    private final String email;
+    private final String phoneNumber;
+    private final String externalKey;
+    private final String locale;
+    private final Currency currency;
+
+    public PaymentProviderContactData(String firstName,
+                                      String lastName,
+                                      String email,
+                                      String phoneNumber,
+                                      String externalKey,
+                                      String locale,
+                                      Currency currency) {
+        this.firstName = StringUtils.substring(firstName, 0, 100);
+        this.lastName  = StringUtils.substring(lastName, 0, 100);
+        this.email     = StringUtils.substring(email, 0, 80);
+        this.phoneNumber = phoneNumber;
+        this.externalKey = externalKey;
+        this.locale = locale;
+        this.currency = currency;
+    }
+
+    public String getFirstName() {
+        return firstName;
+    }
+
+    public String getLastName() {
+        return lastName;
+    }
+
+    public String getEmail() {
+        return email;
+    }
+
+    public String getPhoneNumber() {
+        return phoneNumber;
+    }
+
+    public String getExternalKey() {
+        return externalKey;
+    }
+
+    public String getLocale() {
+        return locale;
+    }
+
+    public Currency getCurrency() {
+        return currency;
+    }
+
+    public static class Builder {
+        private String firstName;
+        private String lastName;
+        private String email;
+        private String phoneNumber;
+        private String externalKey;
+        private String locale;
+        private Currency currency;
+
+        public Builder setExternalKey(String externalKey) {
+            this.externalKey = externalKey;
+            return this;
+        }
+
+        public Builder setFirstName(String firstName) {
+            this.firstName = firstName;
+            return this;
+        }
+
+        public Builder setLastName(String lastName) {
+            this.lastName = lastName;
+            return this;
+        }
+
+        public Builder setEmail(String email) {
+            this.email = email;
+            return this;
+        }
+
+        public Builder setPhoneNumber(String phoneNumber) {
+            this.phoneNumber = phoneNumber;
+            return this;
+        }
+
+        public Builder setLocale(String locale) {
+            this.locale = locale;
+            return this;
+        }
+
+        public Builder setCurrency(Currency currency) {
+            this.currency = currency;
+            return this;
+        }
+
+        public PaymentProviderContactData build() {
+            return new PaymentProviderContactData(firstName, lastName, email, phoneNumber, externalKey, locale, currency);
+        }
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hashCode(firstName,
+                                lastName,
+                                email,
+                                phoneNumber,
+                                externalKey,
+                                locale,
+                                currency);
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (getClass() == obj.getClass()) {
+            PaymentProviderContactData other = (PaymentProviderContactData)obj;
+            if (obj == other) {
+                return true;
+            }
+            else {
+                return Objects.equal(firstName, other.firstName) &&
+                       Objects.equal(lastName, other.lastName) &&
+                       Objects.equal(email, other.email) &&
+                       Objects.equal(phoneNumber, other.phoneNumber) &&
+                       Objects.equal(externalKey, other.externalKey) &&
+                       Objects.equal(locale, other.locale) &&
+                       Objects.equal(currency, other.currency);
+            }
+        }
+        return false;
+    }
+
+}
diff --git a/api/src/main/java/com/ning/billing/payment/api/PaymentService.java b/api/src/main/java/com/ning/billing/payment/api/PaymentService.java
new file mode 100644
index 0000000..988a00a
--- /dev/null
+++ b/api/src/main/java/com/ning/billing/payment/api/PaymentService.java
@@ -0,0 +1,26 @@
+/*
+ * 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.payment.api;
+
+import com.ning.billing.lifecycle.KillbillService;
+
+public interface PaymentService extends KillbillService {
+    @Override
+    String getName();
+
+    PaymentApi getPaymentApi();
+}
diff --git a/api/src/main/java/com/ning/billing/payment/api/PaypalPaymentMethodInfo.java b/api/src/main/java/com/ning/billing/payment/api/PaypalPaymentMethodInfo.java
new file mode 100644
index 0000000..65c36e9
--- /dev/null
+++ b/api/src/main/java/com/ning/billing/payment/api/PaypalPaymentMethodInfo.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.payment.api;
+
+import com.google.common.base.Strings;
+
+
+public final class PaypalPaymentMethodInfo extends PaymentMethodInfo {
+    public static final class Builder extends BuilderBase<PaypalPaymentMethodInfo, Builder> {
+        private String baid;
+        private String email;
+
+        public Builder() {
+            super(Builder.class);
+        }
+
+        public Builder(PaypalPaymentMethodInfo src) {
+            super(Builder.class, src);
+        }
+
+        public Builder setBaid(String baid) {
+            this.baid = baid;
+            return this;
+        }
+
+        public Builder setEmail(String email) {
+            this.email = email;
+            return this;
+        }
+
+        public PaypalPaymentMethodInfo build() {
+            return new PaypalPaymentMethodInfo(id, accountId, defaultMethod, baid, email);
+        }
+    }
+
+    private final String baid;
+    private final String email;
+
+    public PaypalPaymentMethodInfo(String id,
+                                   String accountId,
+                                   Boolean defaultMethod,
+                                   String baid,
+                                   String email) {
+        super(id, accountId, defaultMethod, "PayPal");
+
+        if (Strings.isNullOrEmpty(accountId) || Strings.isNullOrEmpty(baid) || Strings.isNullOrEmpty(email)) {
+            throw new IllegalArgumentException("accountId, baid and email should be present");
+        }
+
+        this.baid = baid;
+        this.email = email;
+    }
+
+    public String getBaid() {
+        return baid;
+    }
+
+    public String getEmail() {
+        return email;
+    }
+}

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

diff --git a/beatrix/pom.xml b/beatrix/pom.xml
index e0e4352..7c9d936 100644
--- a/beatrix/pom.xml
+++ b/beatrix/pom.xml
@@ -13,7 +13,7 @@
     <parent>
         <groupId>com.ning.billing</groupId>
         <artifactId>killbill</artifactId>
-        <version>0.1.2-SNAPSHOT</version>
+        <version>0.1.3-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 1b5b8d6..1324140 100644
--- a/catalog/pom.xml
+++ b/catalog/pom.xml
@@ -13,7 +13,7 @@
     <parent>
         <groupId>com.ning.billing</groupId>
         <artifactId>killbill</artifactId>
-        <version>0.1.2-SNAPSHOT</version>
+        <version>0.1.3-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>killbill-catalog</artifactId>
diff --git a/entitlement/pom.xml b/entitlement/pom.xml
index f0d0aae..4ed28e9 100644
--- a/entitlement/pom.xml
+++ b/entitlement/pom.xml
@@ -13,7 +13,7 @@
     <parent>
         <groupId>com.ning.billing</groupId>
         <artifactId>killbill</artifactId>
-        <version>0.1.2-SNAPSHOT</version>
+        <version>0.1.3-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>killbill-entitlement</artifactId>
@@ -52,6 +52,11 @@
         </dependency>
         <dependency>
             <groupId>com.ning.billing</groupId>
+            <artifactId>killbill-account</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>com.ning.billing</groupId>
             <artifactId>killbill-util</artifactId>
             <type>test-jar</type>
             <scope>test</scope>
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/api/billing/BrainDeadAccount.java b/entitlement/src/test/java/com/ning/billing/entitlement/api/billing/BrainDeadAccount.java
index cc08699..da35883 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/api/billing/BrainDeadAccount.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/api/billing/BrainDeadAccount.java
@@ -21,6 +21,7 @@ import java.util.List;
 import java.util.UUID;
 
 import org.joda.time.DateTime;
+import org.joda.time.DateTimeZone;
 
 import com.ning.billing.account.api.Account;
 import com.ning.billing.catalog.api.Currency;
@@ -148,7 +149,17 @@ public class BrainDeadAccount implements Account {
 	@Override
 	public void addFields(List<CustomField> fields) {
 		throw new UnsupportedOperationException();
-		
+
 	}
 
+    @Override
+    public DateTime getCreatedDate() {
+        return new DateTime(DateTimeZone.UTC);
+    }
+
+    @Override
+    public DateTime getUpdatedDate() {
+        return new DateTime(DateTimeZone.UTC);
+    }
+
 }
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/api/TestApiBase.java b/entitlement/src/test/java/com/ning/billing/entitlement/api/TestApiBase.java
index 9551bcd..75dd17f 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/api/TestApiBase.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/api/TestApiBase.java
@@ -16,11 +16,16 @@
 
 package com.ning.billing.entitlement.api;
 
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertFalse;
+import static org.testng.Assert.assertNotNull;
+import static org.testng.Assert.assertTrue;
+
 import java.io.IOException;
-import java.lang.reflect.Method;
 import java.net.URL;
 import java.util.List;
 import java.util.UUID;
+
 import org.joda.time.DateTime;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -29,6 +34,7 @@ import org.testng.annotations.AfterClass;
 import org.testng.annotations.AfterMethod;
 import org.testng.annotations.BeforeClass;
 import org.testng.annotations.BeforeMethod;
+
 import com.google.inject.Injector;
 import com.ning.billing.account.api.AccountData;
 import com.ning.billing.catalog.DefaultCatalogService;
@@ -43,7 +49,6 @@ import com.ning.billing.catalog.api.ProductCategory;
 import com.ning.billing.catalog.api.TimeUnit;
 import com.ning.billing.config.EntitlementConfig;
 import com.ning.billing.entitlement.api.ApiTestListener.NextEvent;
-import com.ning.billing.entitlement.api.EntitlementService;
 import com.ning.billing.entitlement.api.billing.EntitlementBillingApi;
 import com.ning.billing.entitlement.api.migration.EntitlementMigrationApi;
 import com.ning.billing.entitlement.api.user.EntitlementUserApi;
@@ -64,11 +69,6 @@ import com.ning.billing.util.clock.ClockMock;
 import com.ning.billing.util.eventbus.DefaultEventBusService;
 import com.ning.billing.util.eventbus.BusService;
 
-import static org.testng.Assert.assertEquals;
-import static org.testng.Assert.assertFalse;
-import static org.testng.Assert.assertNotNull;
-import static org.testng.Assert.assertTrue;
-
 
 public abstract class TestApiBase {
 
@@ -289,22 +289,27 @@ public abstract class TestApiBase {
             public String getEmail() {
                 return "accountName@yahoo.com";
             }
+
             @Override
             public String getPhone() {
                 return "4152876341";
             }
+
             @Override
             public String getExternalKey() {
                 return "k123456";
             }
+
             @Override
             public int getBillCycleDay() {
                 return 1;
             }
+
             @Override
             public Currency getCurrency() {
                 return Currency.USD;
             }
+
             @Override
             public String getPaymentProviderName() {
                 return "Paypal";
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/glue/MockEngineModule.java b/entitlement/src/test/java/com/ning/billing/entitlement/glue/MockEngineModule.java
index b57d15b..b647e96 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/glue/MockEngineModule.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/glue/MockEngineModule.java
@@ -16,7 +16,7 @@
 
 package com.ning.billing.entitlement.glue;
 
-import com.ning.billing.account.glue.AccountModuleMock;
+import com.ning.billing.account.glue.AccountModuleWithMocks;
 import com.ning.billing.catalog.glue.CatalogModule;
 import com.ning.billing.util.clock.Clock;
 import com.ning.billing.util.clock.ClockMock;
@@ -32,7 +32,7 @@ public class MockEngineModule extends EntitlementModule {
         bind(Clock.class).to(ClockMock.class).asEagerSingleton();
         install(new EventBusModule());
         install(new CatalogModule());
-        install(new AccountModuleMock());
+        install(new AccountModuleWithMocks());
     }
 
 }

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

diff --git a/invoice/pom.xml b/invoice/pom.xml
index 3cc522b..f153db0 100644
--- a/invoice/pom.xml
+++ b/invoice/pom.xml
@@ -13,7 +13,7 @@
     <parent>
         <groupId>com.ning.billing</groupId>
         <artifactId>killbill</artifactId>
-        <version>0.1.2-SNAPSHOT</version>
+        <version>0.1.3-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>killbill-invoice</artifactId>
@@ -53,7 +53,6 @@
             <type>test-jar</type>
             <scope>test</scope>
         </dependency>
-        
         <dependency>
             <groupId>org.testng</groupId>
             <artifactId>testng</artifactId>
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 835fce9..340e682 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
@@ -1,4 +1,5 @@
 /*
+
  * Copyright 2010-2011 Ning, Inc.
  *
  * Ning licenses this file to you under the Apache License, version 2.0
@@ -16,16 +17,18 @@
 
 package com.ning.billing.invoice.api.invoice;
 
-import java.math.BigDecimal;
-import java.util.List;
-import java.util.UUID;
-import org.joda.time.DateTime;
-import org.skife.jdbi.v2.IDBI;
 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.InvoicePayment;
 import com.ning.billing.invoice.api.InvoicePaymentApi;
 import com.ning.billing.invoice.dao.InvoiceDao;
+import com.ning.billing.invoice.model.DefaultInvoicePayment;
+import org.joda.time.DateTime;
+
+import java.math.BigDecimal;
+import java.util.List;
+import java.util.UUID;
 
 public class DefaultInvoicePaymentApi implements InvoicePaymentApi {
     private final InvoiceDao dao;
@@ -36,15 +39,14 @@ public class DefaultInvoicePaymentApi implements InvoicePaymentApi {
     }
 
     @Override
-    public void paymentSuccessful(final UUID invoiceId, final BigDecimal amount, final Currency currency,
-                                  final UUID paymentId, final DateTime paymentAttemptDate) {
-        dao.notifySuccessfulPayment(invoiceId, amount, currency, paymentId, paymentAttemptDate);
+    public void notifyOfPaymentAttempt(InvoicePayment invoicePayment) {
+        dao.notifyOfPaymentAttempt(invoicePayment);
     }
 
-    @Override
-    public void paymentFailed(final UUID invoiceId, final UUID paymentId, final DateTime paymentAttemptDate) {
-        dao.notifyFailedPayment(invoiceId, paymentId, paymentAttemptDate);
-    }
+//    @Override
+//    public void paymentFailed(UUID invoiceId, UUID paymentId, DateTime paymentAttemptDate) {
+//        dao.notifyFailedPayment(invoiceId.toString(), paymentId.toString(), paymentAttemptDate.toDate());
+//    }
 
     @Override
     public List<Invoice> getInvoicesByAccount(final UUID accountId) {
@@ -55,4 +57,27 @@ public class DefaultInvoicePaymentApi implements InvoicePaymentApi {
     public Invoice getInvoice(final UUID invoiceId) {
         return dao.getById(invoiceId);
     }
-}
+
+    @Override
+    public Invoice getInvoiceForPaymentAttemptId(UUID paymentAttemptId) {
+        UUID invoiceIdStr = dao.getInvoiceIdByPaymentAttemptId(paymentAttemptId);
+        return invoiceIdStr == null ? null : dao.getById(invoiceIdStr);
+    }
+
+    @Override
+    public InvoicePayment getInvoicePayment(UUID paymentAttemptId) {
+        return dao.getInvoicePayment(paymentAttemptId);
+    }
+
+    @Override
+    public void notifyOfPaymentAttempt(UUID invoiceId, BigDecimal amount, Currency currency, UUID paymentAttemptId, DateTime paymentAttemptDate) {
+        InvoicePayment invoicePayment = new DefaultInvoicePayment(paymentAttemptId, invoiceId, paymentAttemptDate, amount, currency, null, null);
+        dao.notifyOfPaymentAttempt(invoicePayment);
+    }
+
+    @Override
+    public void notifyOfPaymentAttempt(UUID invoiceId, UUID paymentAttemptId, DateTime paymentAttemptDate) {
+        InvoicePayment invoicePayment = new DefaultInvoicePayment(paymentAttemptId, invoiceId, paymentAttemptDate);
+        dao.notifyOfPaymentAttempt(invoicePayment);
+    }
+}
\ No newline at end of file
diff --git a/invoice/src/main/java/com/ning/billing/invoice/api/user/DefaultInvoiceCreationNotification.java b/invoice/src/main/java/com/ning/billing/invoice/api/user/DefaultInvoiceCreationNotification.java
index 094e6f1..5c6785c 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/api/user/DefaultInvoiceCreationNotification.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/api/user/DefaultInvoiceCreationNotification.java
@@ -18,7 +18,9 @@ package com.ning.billing.invoice.api.user;
 
 import java.math.BigDecimal;
 import java.util.UUID;
+
 import org.joda.time.DateTime;
+
 import com.ning.billing.catalog.api.Currency;
 import com.ning.billing.invoice.api.InvoiceCreationNotification;
 
@@ -61,4 +63,10 @@ public class DefaultInvoiceCreationNotification implements InvoiceCreationNotifi
     public DateTime getInvoiceCreationDate() {
         return invoiceCreationDate;
     }
+
+    @Override
+    public String toString() {
+        return "DefaultInvoiceCreationNotification [invoiceId=" + invoiceId + ", accountId=" + accountId + ", amountOwed=" + amountOwed + ", currency=" + currency + ", invoiceCreationDate=" + invoiceCreationDate + "]";
+    }
+
 }
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 957294a..9a353ff 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,9 +20,10 @@ import java.math.BigDecimal;
 import java.util.Collection;
 import java.util.List;
 import java.util.UUID;
+
+import com.ning.billing.invoice.api.InvoicePayment;
 import org.joda.time.DateTime;
 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.InvoiceItem;
 import com.ning.billing.invoice.api.InvoiceUserApi;
@@ -52,6 +53,11 @@ public class DefaultInvoiceUserApi implements InvoiceUserApi {
     }
 
     @Override
+    public void notifyOfPaymentAttempt(InvoicePayment invoicePayment) {
+        dao.notifyOfPaymentAttempt(invoicePayment);
+    }
+    
+    @Override
     public BigDecimal getAccountBalance(final UUID accountId) {
         return dao.getAccountBalance(accountId);
     }
@@ -67,17 +73,6 @@ public class DefaultInvoiceUserApi implements InvoiceUserApi {
     }
 
     @Override
-    public void paymentAttemptFailed(final UUID invoiceId, final UUID paymentId, final DateTime paymentAttemptDate) {
-        dao.notifyFailedPayment(invoiceId, paymentId, paymentAttemptDate);
-    }
-
-    @Override
-    public void paymentAttemptSuccessful(final UUID invoiceId, final BigDecimal amount, final Currency currency,
-                                         final UUID paymentId, final DateTime paymentDate) {
-        dao.notifySuccessfulPayment(invoiceId, amount, currency, paymentId, paymentDate);
-    }
-
-    @Override
     public Collection<Invoice> getUnpaidInvoicesByAccountId(final UUID accountId, final DateTime upToDate) {
         return dao.getUnpaidInvoicesByAccountId(accountId, upToDate);
     }
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 0b6174e..6e720f0 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
@@ -27,7 +27,6 @@ import org.skife.jdbi.v2.IDBI;
 import org.skife.jdbi.v2.Transaction;
 import org.skife.jdbi.v2.TransactionStatus;
 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.InvoiceCreationNotification;
 import com.ning.billing.invoice.api.InvoiceItem;
@@ -39,6 +38,7 @@ import com.ning.billing.util.eventbus.Bus;
 public class DefaultInvoiceDao implements InvoiceDao {
     private final InvoiceSqlDao invoiceSqlDao;
     private final InvoiceItemSqlDao invoiceItemSqlDao;
+    private final InvoicePaymentSqlDao invoicePaymentSqlDao;
     private final NextBillingDateNotifier notifier;
     private final EntitlementBillingApi entitlementBillingApi;
 
@@ -49,6 +49,7 @@ public class DefaultInvoiceDao implements InvoiceDao {
                              final NextBillingDateNotifier notifier, final EntitlementBillingApi entitlementBillingApi) {
         this.invoiceSqlDao = dbi.onDemand(InvoiceSqlDao.class);
         this.invoiceItemSqlDao = dbi.onDemand(InvoiceItemSqlDao.class);
+        this.invoicePaymentSqlDao = dbi.onDemand(InvoicePaymentSqlDao.class);
         this.eventBus = eventBus;
         this.notifier = notifier;
         this.entitlementBillingApi = entitlementBillingApi;
@@ -183,17 +184,10 @@ public class DefaultInvoiceDao implements InvoiceDao {
     public BigDecimal getAccountBalance(final UUID accountId) {
         return invoiceSqlDao.getAccountBalance(accountId.toString());
     }
-
+    
     @Override
-    public void notifySuccessfulPayment(final UUID invoiceId, final BigDecimal paymentAmount,
-                                        final Currency currency, final UUID paymentId, final DateTime paymentDate) {
-        invoiceSqlDao.notifySuccessfulPayment(invoiceId.toString(), paymentAmount, currency.toString(),
-                                              paymentId.toString(), paymentDate.toDate());
-    }
-
-    @Override
-    public void notifyFailedPayment(final UUID invoiceId, final UUID paymentId, final DateTime paymentAttemptDate) {
-        invoiceSqlDao.notifyFailedPayment(invoiceId.toString(), paymentId.toString(), paymentAttemptDate.toDate());
+    public void notifyOfPaymentAttempt(InvoicePayment invoicePayment) {
+        invoicePaymentSqlDao.notifyOfPaymentAttempt(invoicePayment);
     }
 
     @Override
@@ -201,7 +195,7 @@ public class DefaultInvoiceDao implements InvoiceDao {
         return invoiceSqlDao.inTransaction(new Transaction<List<Invoice>, InvoiceSqlDao>() {
             @Override
             public List<Invoice> inTransaction(final InvoiceSqlDao invoiceDao, final TransactionStatus status) throws Exception {
-                List<Invoice> invoices = invoiceDao.getUnpaidInvoicesByAccountId(accountId.toString(), upToDate.toDate());
+                List<Invoice> invoices = invoiceSqlDao.getUnpaidInvoicesByAccountId(accountId.toString(), upToDate.toDate());
 
                 getInvoiceItemsWithinTransaction(invoices, invoiceDao);
                 getInvoicePaymentsWithinTransaction(invoices, invoiceDao);
@@ -210,6 +204,16 @@ public class DefaultInvoiceDao implements InvoiceDao {
             }
         });
     }
+    
+    @Override
+    public UUID getInvoiceIdByPaymentAttemptId(UUID paymentAttemptId) {
+        return invoiceSqlDao.getInvoiceIdByPaymentAttemptId(paymentAttemptId.toString());
+    }
+
+    @Override
+    public InvoicePayment getInvoicePayment(UUID paymentAttemptId) {
+        return invoicePaymentSqlDao.getInvoicePayment(paymentAttemptId);
+    }
 
     @Override
     public void test() {
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 1c2f93e..7a7c280 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,9 +16,9 @@
 
 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.api.InvoicePayment;
 import org.joda.time.DateTime;
 
 import java.math.BigDecimal;
@@ -43,17 +43,13 @@ public interface InvoiceDao {
     List<UUID> getInvoicesForPayment(final DateTime targetDate,
                                      final int numberOfDays);
 
-    BigDecimal getAccountBalance(final UUID accountId);
+    UUID getInvoiceIdByPaymentAttemptId(final UUID paymentAttemptId);
+
+    InvoicePayment getInvoicePayment(final UUID paymentAttemptId);
 
-    void notifySuccessfulPayment(final UUID invoiceId,
-                                 final BigDecimal paymentAmount,
-                                 final Currency currency,
-                                 final UUID paymentId,
-                                 final DateTime paymentDate);
+    void notifyOfPaymentAttempt(final InvoicePayment invoicePayment);
 
-    void notifyFailedPayment(final UUID invoiceId,
-                             final UUID paymentId,
-                             final DateTime paymentAttemptDate);
+    BigDecimal getAccountBalance(final UUID accountId);
 
     List<Invoice> getUnpaidInvoicesByAccountId(final UUID accountId, final DateTime upToDate);
 
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 389b433..46b12a1 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
@@ -24,64 +24,80 @@ import java.lang.annotation.Target;
 import java.math.BigDecimal;
 import java.sql.ResultSet;
 import java.sql.SQLException;
+import java.sql.Timestamp;
 import java.util.List;
 import java.util.UUID;
+
+import com.ning.billing.catalog.api.Currency;
 import org.joda.time.DateTime;
+import org.joda.time.DateTimeZone;
 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.*;
 import org.skife.jdbi.v2.sqlobject.customizers.RegisterMapper;
 import org.skife.jdbi.v2.sqlobject.stringtemplate.ExternalizedSqlViaStringTemplate3;
 import org.skife.jdbi.v2.tweak.ResultSetMapper;
-import com.ning.billing.catalog.api.Currency;
+
 import com.ning.billing.invoice.api.InvoicePayment;
-import com.ning.billing.util.entity.EntityDao;
 
 @ExternalizedSqlViaStringTemplate3
 @RegisterMapper(InvoicePaymentSqlDao.InvoicePaymentMapper.class)
-public interface InvoicePaymentSqlDao extends EntityDao<InvoicePayment> {
-    @Override
+public interface InvoicePaymentSqlDao {
+    @SqlQuery
+    public InvoicePayment getByPaymentAttemptId(@Bind("paymentAttempt") final String paymentAttemptId);
+
+    @SqlQuery
+    public List<InvoicePayment> get();
+
     @SqlUpdate
-    public void create(@InvoicePaymentBinder final InvoicePayment invoicePayment);
+    public void create(@InvoicePaymentBinder  InvoicePayment invoicePayment);
 
     @SqlBatch
-    void create(@InvoicePaymentBinder final List<InvoicePayment> items);
+    void create(@InvoicePaymentBinder List<InvoicePayment> items);
 
-    @Override
     @SqlUpdate
-    public void update(@InvoicePaymentBinder final InvoicePayment invoicePayment);
+    public void update(@InvoicePaymentBinder  InvoicePayment invoicePayment);
+
+    @SqlQuery
+    public List<InvoicePayment> getPaymentsForInvoice(@Bind("invoiceId") String invoiceId);
 
     @SqlQuery
-    public List<InvoicePayment> getPaymentsForInvoice(@Bind("invoiceId") final String invoiceId);
+    InvoicePayment getInvoicePayment(@Bind("paymentAttemptId") UUID paymentAttemptId);
+
+    @SqlUpdate
+    void notifyOfPaymentAttempt(@InvoicePaymentBinder InvoicePayment invoicePayment);
 
     public static class InvoicePaymentMapper implements ResultSetMapper<InvoicePayment> {
+        private DateTime getDate(ResultSet rs, String fieldName) throws SQLException {
+            final Timestamp resultStamp = rs.getTimestamp(fieldName);
+            return rs.wasNull() ? null : new DateTime(resultStamp).toDateTime(DateTimeZone.UTC);
+        }
+
         @Override
         public InvoicePayment map(int index, ResultSet result, StatementContext context) throws SQLException {
-            final UUID id = UUID.fromString(result.getString("payment_id"));
+            final UUID paymentAttemptId = UUID.fromString(result.getString("payment_attempt_id"));
             final UUID invoiceId = UUID.fromString(result.getString("invoice_id"));
-            final DateTime paymentDate = new DateTime(result.getTimestamp("payment_date"));
+            final DateTime paymentAttemptDate = getDate(result, "payment_attempt_date");
             final BigDecimal amount = result.getBigDecimal("amount");
             final String currencyString = result.getString("currency");
             final Currency currency = (currencyString == null) ? null : Currency.valueOf(currencyString);
+            final DateTime createdDate = getDate(result, "created_date");
+            final DateTime updatedDate = getDate(result, "updated_date");
 
             return new InvoicePayment() {
+                private  DateTime now = new DateTime();
+
                 @Override
-                public UUID getId() {
-                    return id;
+                public UUID getPaymentAttemptId() {
+                    return paymentAttemptId;
                 }
                 @Override
                 public UUID getInvoiceId() {
                     return invoiceId;
                 }
                 @Override
-                public DateTime getPaymentDate() {
-                    return paymentDate;
+                public DateTime getPaymentAttemptDate() {
+                    return paymentAttemptDate;
                 }
                 @Override
                 public BigDecimal getAmount() {
@@ -91,6 +107,14 @@ public interface InvoicePaymentSqlDao extends EntityDao<InvoicePayment> {
                 public Currency getCurrency() {
                     return currency;
                 }
+                @Override
+                public DateTime getCreatedDate() {
+                    return createdDate ;
+                }
+                @Override
+                public DateTime getUpdatedDate() {
+                    return updatedDate;
+                }
             };
         }
     }
@@ -104,11 +128,15 @@ public interface InvoicePaymentSqlDao extends EntityDao<InvoicePayment> {
                 return new Binder<InvoicePaymentBinder, InvoicePayment>() {
                     public void bind(SQLStatement q, InvoicePaymentBinder bind, InvoicePayment payment) {
                         q.bind("invoiceId", payment.getInvoiceId().toString());
-                        q.bind("paymentId", payment.getId().toString());
-                        q.bind("paymentDate", payment.getPaymentDate().toDate());
+                        q.bind("paymentAttemptId", payment.getPaymentAttemptId().toString());
+                        q.bind("paymentDate", payment.getPaymentAttemptDate().toDate());
                         q.bind("amount", payment.getAmount());
                         Currency currency = payment.getCurrency();
                         q.bind("currency", (currency == null) ? null : currency.toString());
+                        DateTime createdDate = payment.getCreatedDate();
+                        q.bind("createdDate", (createdDate == null) ? new DateTime().toDate() : createdDate.toDate());
+                        DateTime updatedDate = payment.getUpdatedDate();
+                        q.bind("updatedDate", (updatedDate == null) ? new DateTime().toDate() : updatedDate.toDate());
                     }
                 };
             }
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 e890413..ccb8eff 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
@@ -24,7 +24,12 @@ 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.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;
@@ -32,7 +37,11 @@ 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.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;
@@ -62,42 +71,40 @@ public interface InvoiceSqlDao extends EntityDao<Invoice>, Transactional<Invoice
     List<Invoice> getInvoicesBySubscription(@Bind("subscriptionId") final String subscriptionId);
 
     @SqlQuery
-    List<Invoice> getUnpaidInvoicesByAccountId(@Bind("accountId") final String accountId,
-                                               @Bind("upToDate") final Date upToDate);
+    @RegisterMapper(UuidMapper.class)
+    UUID getInvoiceIdByPaymentAttemptId(@Bind("paymentAttemptId") final String paymentAttemptId);
 
     @SqlQuery
     @RegisterMapper(UuidMapper.class)
     List<UUID> getInvoicesForPayment(@Bind("targetDate") final Date targetDate,
-                                     @Bind("numberOfDays") final int numberOfDays);
-
+                                    @Bind("numberOfDays") final int numberOfDays);
+    
     @SqlQuery
     @RegisterMapper(BalanceMapper.class)
     BigDecimal getAccountBalance(@Bind("accountId") final String accountId);
 
-    @SqlUpdate
-    void notifySuccessfulPayment(@Bind("invoiceId") final String invoiceId,
-                                 @Bind("amount") 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);
-
+    @SqlQuery
+    List<Invoice> getUnpaidInvoicesByAccountId(@Bind("accountId") final String accountId,
+                                               @Bind("upToDate") final Date upToDate);
+    
     @BindingAnnotation(InvoiceBinder.InvoiceBinderFactory.class)
     @Retention(RetentionPolicy.RUNTIME)
     @Target({ElementType.PARAMETER})
     public @interface InvoiceBinder {
         public static class InvoiceBinderFactory implements BinderFactory {
-            public Binder build(Annotation annotation) {
+            @Override
+            public Binder<InvoiceBinder, Invoice> build(Annotation annotation) {
                 return new Binder<InvoiceBinder, Invoice>() {
-                    public void bind(SQLStatement q, InvoiceBinder bind, Invoice invoice) {
+                    @Override
+                    public void bind(@SuppressWarnings("rawtypes") 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.getBalance());
+                        DateTime last_payment_date = invoice.getLastPaymentAttempt();
+                        q.bind("lastPaymentAttempt", last_payment_date == null ? null : last_payment_date.toDate());
                         q.bind("currency", invoice.getCurrency().toString());
                     }
                 };
@@ -107,7 +114,7 @@ public interface InvoiceSqlDao extends EntityDao<Invoice>, Transactional<Invoice
 
     public static class InvoiceMapper implements ResultSetMapper<Invoice> {
         @Override
-        public Invoice map(final int index, final ResultSet result, final StatementContext context) throws SQLException {
+        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"));
@@ -117,7 +124,7 @@ public interface InvoiceSqlDao extends EntityDao<Invoice>, Transactional<Invoice
             return new DefaultInvoice(id, accountId, invoiceDate, targetDate, currency);
         }
     }
-
+      
     public static class BalanceMapper implements ResultSetMapper<BigDecimal> {
         @Override
         public BigDecimal map(final int index, final ResultSet result, final StatementContext context) throws SQLException {
@@ -133,7 +140,7 @@ public interface InvoiceSqlDao extends EntityDao<Invoice>, Transactional<Invoice
             }
 
             return amount_invoiced.subtract(amount_paid);
-        };
+        }
     }
 }
 
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 ae10d41..8ee40e3 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
@@ -28,14 +28,21 @@ import java.util.ArrayList;
 import java.util.List;
 import java.util.UUID;
 
+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.InvoiceItem;
+import com.ning.billing.util.clock.DefaultClock;
+
 public class DefaultInvoice implements Invoice {
     private final InvoiceItemList invoiceItems = new InvoiceItemList();
     private final List<InvoicePayment> payments = new ArrayList<InvoicePayment>();
     private final UUID id;
-    private UUID accountId;
+    private final UUID accountId;
     private final DateTime invoiceDate;
     private final DateTime targetDate;
-    private Currency currency;
+    private final Currency currency;
 
     public DefaultInvoice(UUID accountId, DateTime targetDate, Currency currency) {
         this(UUID.randomUUID(), accountId, new DefaultClock().getUTCNow(), targetDate, currency);
@@ -120,7 +127,7 @@ public class DefaultInvoice implements Invoice {
         DateTime lastPaymentAttempt = null;
 
         for (final InvoicePayment paymentAttempt : payments) {
-            DateTime paymentAttemptDate = paymentAttempt.getPaymentDate();
+            DateTime paymentAttemptDate = paymentAttempt.getPaymentAttemptDate();
             if (lastPaymentAttempt == null) {
                 lastPaymentAttempt = paymentAttemptDate;
             }
@@ -167,5 +174,11 @@ public class DefaultInvoice implements Invoice {
 
         return lastPaymentAttempt.plusDays(numberOfDays).isBefore(targetDate);
     }
+
+    @Override
+    public String toString() {
+        return "DefaultInvoice [items=" + invoiceItems + ", payments=" + payments + ", id=" + id + ", accountId=" + accountId + ", invoiceDate=" + invoiceDate + ", targetDate=" + targetDate + ", currency=" + currency + ", amountPaid=" + getAmountPaid() + ", lastPaymentAttempt=" + getLastPaymentAttempt() + "]";
+    }
+
 }
 
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 ef1ed29..6760481 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,31 +16,56 @@
 
 package com.ning.billing.invoice.model;
 
-import java.math.BigDecimal;
-import java.util.UUID;
-import org.joda.time.DateTime;
 import com.ning.billing.catalog.api.Currency;
 import com.ning.billing.invoice.api.InvoicePayment;
-import com.ning.billing.util.entity.EntityBase;
+import org.joda.time.DateTime;
 
-public class DefaultInvoicePayment extends EntityBase<InvoicePayment> implements InvoicePayment {
+import javax.annotation.Nullable;
+import java.math.BigDecimal;
+import java.util.UUID;
+
+public class DefaultInvoicePayment implements InvoicePayment {
+    private final UUID paymentAttemptId;
     private final UUID invoiceId;
     private final DateTime paymentDate;
     private final BigDecimal amount;
     private final Currency currency;
+    private final DateTime createdDate;
+    private final DateTime updatedDate;
+
+    public DefaultInvoicePayment(final UUID invoiceId, final DateTime paymentDate) {
+        this(UUID.randomUUID(), invoiceId, paymentDate, null, null, null, null);
+    }
+
+    public DefaultInvoicePayment(final UUID paymentAttemptId, final UUID invoiceId, final DateTime paymentDate) {
+        this(paymentAttemptId, invoiceId, paymentDate, null, null, null, null);
+    }
 
     public DefaultInvoicePayment(final UUID invoiceId, final DateTime paymentDate,
                                  final BigDecimal amount, final Currency currency) {
-        this(UUID.randomUUID(), invoiceId, paymentDate, amount, currency);
+        this(UUID.randomUUID(), invoiceId, paymentDate, amount, currency, null, null);
     }
 
-    public DefaultInvoicePayment(final UUID id, final UUID invoiceId, final DateTime paymentDate,
+    public DefaultInvoicePayment(final UUID paymentAttemptId, final UUID invoiceId, final DateTime paymentDate,
                                  final BigDecimal amount, final Currency currency) {
-        super(id);
+        this(paymentAttemptId, invoiceId, paymentDate, amount, currency, null, null);
+    }
+
+    public DefaultInvoicePayment(final UUID paymentAttemptId, final UUID invoiceId, final DateTime paymentDate,
+                                 @Nullable final BigDecimal amount, @Nullable final Currency currency,
+                                 @Nullable final DateTime createdDate, @Nullable final DateTime updatedDate) {
+        this.paymentAttemptId = paymentAttemptId;
         this.amount = amount;
         this.invoiceId = invoiceId;
         this.paymentDate = paymentDate;
         this.currency = currency;
+        this.createdDate = (createdDate == null) ? new DateTime() : createdDate;
+        this.updatedDate = (updatedDate == null) ? new DateTime() : updatedDate;
+    }
+
+    @Override
+    public UUID getPaymentAttemptId() {
+        return paymentAttemptId;
     }
 
     @Override
@@ -49,7 +74,7 @@ public class DefaultInvoicePayment extends EntityBase<InvoicePayment> implements
     }
 
     @Override
-    public DateTime getPaymentDate() {
+    public DateTime getPaymentAttemptDate() {
         return paymentDate;
     }
 
@@ -62,4 +87,14 @@ public class DefaultInvoicePayment extends EntityBase<InvoicePayment> implements
     public Currency getCurrency() {
         return currency;
     }
+
+    @Override
+    public DateTime getCreatedDate() {
+        return createdDate;
+    }
+
+    @Override
+    public DateTime getUpdatedDate() {
+        return updatedDate;
+    }
 }
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 ef91849..626f8a2 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
@@ -1,29 +1,39 @@
 group InvoicePayment;
 
+invoicePaymentFields(prefix) :== <<
+  <prefix>invoice_id,
+  <prefix>payment_attempt_id,
+  <prefix>payment_attempt_date,
+  <prefix>amount,
+  <prefix>currency,
+  <prefix>created_date,
+  <prefix>updated_date
+>>
+
 create() ::= <<
-  INSERT INTO invoice_payments(invoice_id, payment_id, payment_date, amount, currency)
-  VALUES(:invoiceId, :paymentId, :paymentDate, :amount, :currency)
+  INSERT INTO invoice_payments(<invoicePaymentFields()>)
+  VALUES(:invoiceId, :paymentAttemptId, :paymentDate, :amount, :currency, :createdDate, :updatedDate)
 >>
 
 update() ::= <<
   UPDATE invoice_payments
-  SET payment_date = :paymentDate, amount = :amount, currency = :currency
-  WHERE invoice_id = :invoiceId, payment_id = :paymentId
+  SET payment_date = :paymentDate, amount = :amount, currency = :currency, created_date = :createdDate, updated_date = :updatedDate
+  WHERE invoice_id = :invoiceId, payment_attempt_id = :paymentAttemptId
 >>
 
-getById() ::= <<
-  SELECT invoice_id, payment_id, payment_date, amount, currency
+getByPaymentAttemptId() ::= <<
+  SELECT invoice_payments(<invoicePaymentFields()>)
   FROM invoice_payments
-  WHERE payment_id = :id
+  WHERE payment_id = :paymentAttemptId
 >>
 
 get() ::= <<
-  SELECT invoice_id, payment_id, payment_date, amount, currency
+  SELECT invoice_payments(<invoicePaymentFields()>)
   FROM invoice_payments
 >>
 
 getPaymentsForInvoice() ::= <<
-  SELECT invoice_id, payment_id, payment_date, amount, currency
+  SELECT invoice_payments(<invoicePaymentFields()>)
   FROM invoice_payments
   WHERE invoice_id = :invoiceId
 >>
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 872cc79..6b0c899 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
@@ -29,7 +29,8 @@ getInvoicesByAccountAfterDate() ::= <<
 >>
 
 getInvoicesBySubscription() ::= <<
-  SELECT <invoiceFields("i.")>
+  SELECT i.id, i.account_id, i.invoice_date, i.target_date, i.currency, SUM(ii.amount) AS amount,
+         SUM(ip.amount) AS amount_paid, MAX(ip.payment_date) AS last_payment_attempt
   FROM invoices i
   LEFT JOIN invoice_items ii ON i.id = ii.invoice_id
   WHERE ii.subscription_id = :subscriptionId
@@ -67,20 +68,28 @@ create() ::= <<
   VALUES (:id, :accountId, :invoiceDate, :targetDate, :currency);
 >>
 
+getInvoiceIdByPaymentAttemptId() ::= <<
+  SELECT i.id
+    FROM invoices i, invoice_payments ip
+   WHERE ip.invoice_id = i.id
+     AND ip.payment_attempt_id = :paymentAttemptId
+>>
+
 update() ::= <<
   UPDATE invoices
   SET account_id = :accountId, invoice_date = :invoiceDate, target_date = :targetDate, currency = :currency
   WHERE id = :id;
 >>
 
-notifySuccessfulPayment() ::= <<
-  INSERT INTO invoice_payments(invoice_id, payment_id, payment_date, amount, currency)
-  VALUES(:invoiceId, :paymentId, :paymentDate, :amount, :currency);
+notifyOfPaymentAttempt() ::= <<
+  INSERT INTO invoice_payments(invoice_id, payment_attempt_id, payment_attempt_date, amount, currency, created_date, updated_date)
+  VALUES(:invoice_id, :payment_attempt_id, :payment_attempt_date, :amount, :currency, NOW(), NOW());
 >>
 
-notifyFailedPayment() ::= <<
-  INSERT INTO invoice_payments(invoice_id, payment_id, payment_date)
-  VALUES(:invoiceId, :paymentId, :paymentAttemptDate);
+getInvoicePayment() ::= <<
+    SELECT invoice_id, payment_attempt_id, payment_attempt_date, amount, currency, created_date, updated_date
+      FROM invoice_payments
+     WHERE payment_id = :payment_id
 >>
 
 getUnpaidInvoicesByAccountId() ::= <<
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 f8e6cfb..c2995c7 100644
--- a/invoice/src/main/resources/com/ning/billing/invoice/ddl.sql
+++ b/invoice/src/main/resources/com/ning/billing/invoice/ddl.sql
@@ -29,17 +29,19 @@ CREATE INDEX invoices_account_id ON invoices(account_id ASC);
 DROP TABLE IF EXISTS invoice_payments;
 CREATE TABLE invoice_payments (
   invoice_id char(36) NOT NULL,
-  payment_id char(36) NOT NULL,
-  payment_date datetime NOT NULL,
+  payment_attempt_id char(36) COLLATE utf8_bin NOT NULL,
+  payment_attempt_date datetime,
   amount numeric(10,4),
   currency char(3),
-  PRIMARY KEY(invoice_id, payment_id)
+  created_date datetime NOT NULL,
+  updated_date datetime NOT NULL,
+  PRIMARY KEY(invoice_id, payment_attempt_id)
 ) ENGINE=innodb;
-CREATE UNIQUE INDEX invoice_payments_unique ON invoice_payments(invoice_id, payment_id);
+CREATE UNIQUE INDEX invoice_payments_unique ON invoice_payments(invoice_id, payment_attempt_id);
 
 DROP VIEW IF EXISTS invoice_payment_summary;
 CREATE VIEW invoice_payment_summary AS
-SELECT invoice_id, SUM(amount) AS total_paid, MAX(payment_date) AS last_payment_date
+SELECT invoice_id, SUM(amount) AS total_paid, MAX(payment_attempt_date) AS last_payment_date
 FROM invoice_payments
 GROUP BY invoice_id;
 
@@ -47,4 +49,4 @@ DROP VIEW IF EXISTS invoice_item_summary;
 CREATE VIEW invoice_item_summary AS
 select invoice_id, sum(amount) AS total_amount
 from invoice_items
-group by invoice_id;
\ No newline at end of file
+group by invoice_id;
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
new file mode 100644
index 0000000..e8f66b1
--- /dev/null
+++ b/invoice/src/test/java/com/ning/billing/invoice/api/MockInvoicePaymentApi.java
@@ -0,0 +1,102 @@
+/*
+ * 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.api;
+
+import java.math.BigDecimal;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.UUID;
+import java.util.concurrent.CopyOnWriteArrayList;
+
+import com.ning.billing.invoice.model.DefaultInvoicePayment;
+import org.joda.time.DateTime;
+
+import com.ning.billing.catalog.api.Currency;
+
+public class MockInvoicePaymentApi implements InvoicePaymentApi
+{
+    private final CopyOnWriteArrayList<Invoice> invoices = new CopyOnWriteArrayList<Invoice>();
+    private final CopyOnWriteArrayList<InvoicePayment> invoicePayments = new CopyOnWriteArrayList<InvoicePayment>();
+
+    public void add(Invoice invoice) {
+        invoices.add(invoice);
+    }
+
+    @Override
+    public void notifyOfPaymentAttempt(InvoicePayment invoicePayment) {
+        for (InvoicePayment existingInvoicePayment : invoicePayments) {
+            if (existingInvoicePayment.getInvoiceId().equals(invoicePayment.getInvoiceId()) && existingInvoicePayment.getPaymentAttemptId().equals(invoicePayment.getPaymentAttemptId())) {
+                invoicePayments.remove(existingInvoicePayment);
+            }
+        }
+        invoicePayments.add(invoicePayment);
+    }
+
+    @Override
+    public List<Invoice> getInvoicesByAccount(UUID accountId) {
+        ArrayList<Invoice> result = new ArrayList<Invoice>();
+
+        for (Invoice invoice : invoices) {
+            if (accountId.equals(invoice.getAccountId())) {
+                result.add(invoice);
+            }
+        }
+        return result;
+    }
+
+    @Override
+    public Invoice getInvoice(UUID invoiceId) {
+        for (Invoice invoice : invoices) {
+            if (invoiceId.equals(invoice.getId())) {
+                return invoice;
+            }
+        }
+        return null;
+    }
+
+    @Override
+    public Invoice getInvoiceForPaymentAttemptId(UUID paymentAttemptId) {
+        for (InvoicePayment invoicePayment : invoicePayments) {
+            if (invoicePayment.getPaymentAttemptId().equals(paymentAttemptId)) {
+                return getInvoice(invoicePayment.getInvoiceId());
+            }
+        }
+        return null;
+    }
+
+    @Override
+    public InvoicePayment getInvoicePayment(UUID paymentAttemptId) {
+        for (InvoicePayment invoicePayment : invoicePayments) {
+            if (paymentAttemptId.equals(invoicePayment.getPaymentAttemptId())) {
+                return invoicePayment;
+            }
+        }
+        return null;
+    }
+
+    @Override
+    public void notifyOfPaymentAttempt(UUID invoiceId, BigDecimal amountOutstanding, Currency currency, UUID paymentAttemptId, DateTime paymentAttemptDate) {
+        InvoicePayment invoicePayment = new DefaultInvoicePayment(paymentAttemptId, invoiceId, paymentAttemptDate, amountOutstanding, currency);
+        notifyOfPaymentAttempt(invoicePayment);
+    }
+
+    @Override
+    public void notifyOfPaymentAttempt(UUID invoiceId, UUID paymentAttemptId, DateTime paymentAttemptDate) {
+        InvoicePayment invoicePayment = new DefaultInvoicePayment(paymentAttemptId, invoiceId, paymentAttemptDate);
+        notifyOfPaymentAttempt(invoicePayment);
+    }
+}
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 26d97af..fae8e44 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
@@ -19,7 +19,7 @@ package com.ning.billing.invoice.dao;
 import com.google.inject.Guice;
 import com.google.inject.Injector;
 import com.google.inject.Stage;
-import com.ning.billing.invoice.glue.InvoiceModuleMock;
+import com.ning.billing.invoice.glue.InvoiceModuleWithEmbeddedDb;
 import com.ning.billing.util.eventbus.BusService;
 import com.ning.billing.util.eventbus.DefaultEventBusService;
 import org.apache.commons.io.IOUtils;
@@ -35,13 +35,13 @@ public abstract class InvoiceDaoTestBase {
     protected InvoiceDao invoiceDao;
     protected InvoiceItemSqlDao invoiceItemDao;
     protected InvoicePaymentSqlDao invoicePaymentDao;
-    protected InvoiceModuleMock module;
+    protected InvoiceModuleWithEmbeddedDb module;
 
     @BeforeClass(alwaysRun = true)
     protected void setup() throws IOException {
         // Health check test to make sure MySQL is setup properly
         try {
-            module = new InvoiceModuleMock();
+            module = new InvoiceModuleWithEmbeddedDb();
             final String invoiceDdl = IOUtils.toString(DefaultInvoiceDao.class.getResourceAsStream("/com/ning/billing/invoice/ddl.sql"));
             final String entitlementDdl = IOUtils.toString(DefaultInvoiceDao.class.getResourceAsStream("/com/ning/billing/entitlement/ddl.sql"));
 
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 962b226..cd9e2bf 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,13 +16,6 @@
 
 package com.ning.billing.invoice.dao;
 
-import java.math.BigDecimal;
-import java.util.ArrayList;
-import java.util.Collection;
-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.api.InvoiceItem;
@@ -30,17 +23,23 @@ import com.ning.billing.invoice.api.InvoicePayment;
 import com.ning.billing.invoice.model.DefaultInvoice;
 import com.ning.billing.invoice.model.DefaultInvoiceItem;
 import com.ning.billing.invoice.model.DefaultInvoicePayment;
+import com.ning.billing.util.clock.Clock;
 import com.ning.billing.util.clock.DefaultClock;
+import org.joda.time.DateTime;
+import org.testng.annotations.Test;
+
+import java.math.BigDecimal;
+import java.util.ArrayList;
+import java.util.Collection;
+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.assertNull;
-import static org.testng.Assert.assertTrue;
-import static org.testng.Assert.fail;
+import static org.testng.Assert.*;
 
 @Test(groups = {"invoicing", "invoicing-invoiceDao"})
 public class InvoiceDaoTests extends InvoiceDaoTestBase {
     private final int NUMBER_OF_DAY_BETWEEN_RETRIES = 8;
+    private final Clock clock = new DefaultClock();
 
     @Test
     public void testCreationAndRetrievalByAccount() {
@@ -81,8 +80,9 @@ public class InvoiceDaoTests extends InvoiceDaoTestBase {
         assertEquals(savedInvoice.getInvoiceItems().size(), 1);
 
         BigDecimal paymentAmount = new BigDecimal("11.00");
-        UUID paymentId = UUID.randomUUID();
-        invoiceDao.notifySuccessfulPayment(invoiceId, paymentAmount, Currency.USD, paymentId, new DefaultClock().getUTCNow().plusDays(12));
+        UUID paymentAttemptId = UUID.randomUUID();
+
+        invoiceDao.notifyOfPaymentAttempt(new DefaultInvoicePayment(paymentAttemptId, invoiceId, clock.getUTCNow().plusDays(12), paymentAmount, Currency.USD));
 
         Invoice retrievedInvoice = invoiceDao.getById(invoiceId);
         assertNotNull(retrievedInvoice);
@@ -104,12 +104,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);
 
-        UUID paymentId = UUID.randomUUID();
+        UUID paymentAttemptId = UUID.randomUUID();
         DateTime paymentAttemptDate = new DateTime(2011, 6, 24, 12, 14, 36, 0);
         BigDecimal paymentAmount = new BigDecimal("14.0");
 
         invoiceDao.create(invoice);
-        invoiceDao.notifySuccessfulPayment(invoice.getId(), paymentAmount, Currency.USD, paymentId, paymentAttemptDate);
+        invoiceDao.notifyOfPaymentAttempt(new DefaultInvoicePayment(paymentAttemptId, invoice.getId(), paymentAttemptDate, paymentAmount, Currency.USD));
 
         invoice = invoiceDao.getById(invoice.getId());
         assertEquals(invoice.getAmountPaid().compareTo(paymentAmount), 0);
@@ -126,7 +126,7 @@ public class InvoiceDaoTests extends InvoiceDaoTestBase {
         DateTime paymentAttemptDate = new DateTime(2011, 6, 24, 12, 14, 36, 0);
 
         invoiceDao.create(invoice);
-        invoiceDao.notifyFailedPayment(invoice.getId(), UUID.randomUUID(), paymentAttemptDate);
+        invoiceDao.notifyOfPaymentAttempt(new DefaultInvoicePayment(invoice.getId(), paymentAttemptDate));
 
         invoice = invoiceDao.getById(invoice.getId());
         assertTrue(invoice.getLastPaymentAttempt().equals(paymentAttemptDate));
@@ -136,11 +136,11 @@ public class InvoiceDaoTests extends InvoiceDaoTestBase {
     public void testGetInvoicesForPaymentWithNoResults() {
         DateTime notionalDate = new DateTime();
         DateTime targetDate = new DateTime(2011, 10, 6, 0, 0, 0, 0);
-        
+
         // determine the number of existing invoices available for payment (to avoid side effects from other tests)
         List<UUID> invoices = invoiceDao.getInvoicesForPayment(notionalDate, NUMBER_OF_DAY_BETWEEN_RETRIES);
         int existingInvoiceCount = invoices.size();
-        
+
         UUID accountId = UUID.randomUUID();
         Invoice invoice = new DefaultInvoice(accountId, targetDate, Currency.USD);
 
@@ -178,7 +178,7 @@ public class InvoiceDaoTests extends InvoiceDaoTestBase {
 
         // attempt a payment; ensure that the number of invoices for payment has decreased by 1
         // (no retries for NUMBER_OF_DAYS_BETWEEN_RETRIES days)
-        invoiceDao.notifyFailedPayment(invoice.getId(), UUID.randomUUID(), notionalDate);
+        invoiceDao.notifyOfPaymentAttempt(new DefaultInvoicePayment(invoice.getId(), notionalDate));
         invoices = invoiceDao.getInvoicesForPayment(notionalDate, NUMBER_OF_DAY_BETWEEN_RETRIES);
         count = getInvoicesDueForPaymentAttempt(invoiceDao.get(), notionalDate).size();
         assertEquals(invoices.size(), count);
@@ -191,7 +191,8 @@ public class InvoiceDaoTests extends InvoiceDaoTestBase {
         assertEquals(invoices.size(), count);
 
         // post successful partial payment; ensure that number of invoices for payment has decreased by 1
-        invoiceDao.notifySuccessfulPayment(invoiceId, new BigDecimal("22.0000"), Currency.USD, UUID.randomUUID(), notionalDate);
+        invoiceDao.notifyOfPaymentAttempt(new DefaultInvoicePayment(UUID.randomUUID(), invoice.getId(), notionalDate, new BigDecimal("22.0000"), Currency.USD));
+
         invoices = invoiceDao.getInvoicesForPayment(notionalDate, NUMBER_OF_DAY_BETWEEN_RETRIES);
         count = getInvoicesDueForPaymentAttempt(invoiceDao.get(), notionalDate).size();
         assertEquals(invoices.size(), count);
@@ -208,7 +209,8 @@ public class InvoiceDaoTests extends InvoiceDaoTestBase {
         assertEquals(invoices.size(), count);
 
         // post completed payment; ensure that the number of invoices for payment has decreased by 1
-        invoiceDao.notifySuccessfulPayment(invoiceId, new BigDecimal("5.0000"), Currency.USD, UUID.randomUUID(), notionalDate);
+        invoiceDao.notifyOfPaymentAttempt(new DefaultInvoicePayment(UUID.randomUUID(), invoice.getId(), notionalDate, new BigDecimal("5.0000"), Currency.USD));
+
         invoices = invoiceDao.getInvoicesForPayment(notionalDate, NUMBER_OF_DAY_BETWEEN_RETRIES);
         count = getInvoicesDueForPaymentAttempt(invoiceDao.get(), notionalDate).size();
         assertEquals(invoices.size(), count);
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 fa743ff..dc2afd8 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
@@ -21,7 +21,7 @@ import com.google.inject.Injector;
 import com.google.inject.Stage;
 import com.ning.billing.catalog.api.Currency;
 import com.ning.billing.invoice.api.InvoiceItem;
-import com.ning.billing.invoice.glue.InvoiceModuleMock;
+import com.ning.billing.invoice.glue.InvoiceModuleWithEmbeddedDb;
 import com.ning.billing.invoice.model.DefaultInvoice;
 import com.ning.billing.invoice.model.DefaultInvoiceItem;
 import org.apache.commons.io.IOUtils;
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
new file mode 100644
index 0000000..522ef48
--- /dev/null
+++ b/invoice/src/test/java/com/ning/billing/invoice/dao/MockInvoiceDao.java
@@ -0,0 +1,259 @@
+/*
+ * 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 java.math.BigDecimal;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.UUID;
+
+import com.ning.billing.invoice.api.InvoicePayment;
+import com.ning.billing.util.eventbus.Bus;
+import org.joda.time.DateTime;
+
+import com.google.inject.Inject;
+import com.ning.billing.invoice.api.Invoice;
+import com.ning.billing.invoice.api.InvoiceItem;
+import com.ning.billing.invoice.api.user.DefaultInvoiceCreationNotification;
+
+public class MockInvoiceDao implements InvoiceDao {
+    private final Bus eventBus;
+    private final Object monitor = new Object();
+    private final Map<UUID, Invoice> invoices = new LinkedHashMap<UUID, Invoice>();
+
+    @Inject
+    public MockInvoiceDao(Bus eventBus) {
+        this.eventBus = eventBus;
+    }
+
+    @Override
+    public void create(Invoice invoice) {
+        synchronized (monitor) {
+            invoices.put(invoice.getId(), invoice);
+        }
+        try {
+            eventBus.post(new DefaultInvoiceCreationNotification(invoice.getId(), invoice.getAccountId(),
+                                                                 invoice.getBalance(), invoice.getCurrency(),
+                                                                 invoice.getInvoiceDate()));
+        }
+        catch (Bus.EventBusException ex) {
+            throw new RuntimeException(ex);
+        }
+    }
+
+//    private Invoice munge(Invoice invoice) {
+//        if (invoice == null) {
+//            return null;
+//        }
+//
+//        DateTime lastPaymentDate = null;
+//        BigDecimal amountPaid = new BigDecimal("0");
+//
+//        for (InvoicePayment invoicePayment : invoicePayments.values()) {
+//            if (invoicePayment.getInvoiceId().equals(invoice.getId())) {
+//                if (lastPaymentDate == null || lastPaymentDate.isBefore(invoicePayment.getPaymentAttemptDate())) {
+//                    lastPaymentDate = invoicePayment.getPaymentAttemptDate();
+//                }
+//                if (invoicePayment.getAmount() != null) {
+//                    amountPaid = amountPaid.add(invoicePayment.getAmount());
+//                }
+//            }
+//        }
+//
+//        Invoice newInvoice = new DefaultInvoice(invoice.getId(),
+//                                                invoice.getAccountId(),
+//                                                invoice.getInvoiceDate(),
+//                                                invoice.getTargetDate(),
+//                                                invoice.getCurrency());
+//        newInvoice.addInvoiceItems(invoice.getInvoiceItems());
+//        newInvoice.addPayments(invoice.getPayments());
+//
+//        return newInvoice;
+//    }
+//
+//    private List<Invoice> munge(Collection<Invoice> invoices) {
+//        List<Invoice> result = new ArrayList<Invoice>();
+//        for (Invoice invoice : invoices) {
+//            result.add(munge(invoice));
+//        }
+//        return result;
+//    }
+
+    @Override
+    public Invoice getById(UUID id) {
+        synchronized (monitor) {
+            return invoices.get(id);
+        }
+    }
+
+    @Override
+    public List<Invoice> get() {
+        synchronized (monitor) {
+            return new ArrayList<Invoice>(invoices.values());
+        }
+    }
+
+    @Override
+    public List<Invoice> getInvoicesByAccount(UUID accountId) {
+        List<Invoice> result = new ArrayList<Invoice>();
+
+        synchronized (monitor) {
+            for (Invoice invoice : invoices.values()) {
+                if (accountId.equals(invoice.getAccountId())) {
+                    result.add(invoice);
+                }
+            }
+        }
+        return result;
+    }
+
+    @Override
+    public List<Invoice> getInvoicesByAccount(UUID accountId, DateTime fromDate) {
+        List<Invoice> invoicesForAccount = new ArrayList<Invoice>();
+
+        synchronized (monitor) {
+            for (Invoice invoice : get()) {
+                if (accountId.equals(invoice.getAccountId()) && !invoice.getTargetDate().isBefore(fromDate)) {
+                    invoicesForAccount.add(invoice);
+                }
+            }
+        }
+
+        return invoicesForAccount;
+    }
+
+    @Override
+    public List<InvoiceItem> getInvoiceItemsByAccount(UUID accountId) {
+        List<InvoiceItem> invoiceItemsForAccount = new ArrayList<InvoiceItem>();
+
+        synchronized (monitor) {
+            for (Invoice invoice : get()) {
+                if (accountId.equals(invoice.getAccountId())) {
+                    invoiceItemsForAccount.addAll(invoice.getInvoiceItems());
+                }
+            }
+        }
+
+        return invoiceItemsForAccount;
+    }
+
+    @Override
+    public List<Invoice> getInvoicesBySubscription(UUID subscriptionId) {
+        List<Invoice> result = new ArrayList<Invoice>();
+
+        synchronized (monitor) {
+            for (Invoice invoice : invoices.values()) {
+                for (InvoiceItem item : invoice.getInvoiceItems()) {
+                    if (subscriptionId.equals(item.getSubscriptionId())) {
+                        result.add(invoice);
+                        break;
+                    }
+                }
+            }
+        }
+        return result;
+    }
+
+    @Override
+    public List<UUID> getInvoicesForPayment(DateTime targetDate, int numberOfDays) {
+        List<UUID> result = new ArrayList<UUID>();
+
+        synchronized (monitor) {
+            for (Invoice invoice : invoices.values()) {
+                if (invoice.isDueForPayment(targetDate, numberOfDays)) {
+                    result.add(invoice.getId());
+                }
+            }
+        }
+
+        return result;
+    }
+
+    @Override
+    public void test() {
+    }
+
+    @Override
+    public UUID getInvoiceIdByPaymentAttemptId(UUID paymentAttemptId) {
+        synchronized(monitor) {
+            for (Invoice invoice : invoices.values()) {
+                for (InvoicePayment payment : invoice.getPayments()) {
+                    if (paymentAttemptId.equals(payment.getPaymentAttemptId())) {
+                        return invoice.getId();
+                    }
+                }
+            }
+        }
+        return null;
+    }
+
+    @Override
+    public InvoicePayment getInvoicePayment(UUID paymentAttemptId) {
+        synchronized(monitor) {
+            for (Invoice invoice : invoices.values()) {
+                for (InvoicePayment payment : invoice.getPayments()) {
+                    if (paymentAttemptId.equals(payment.getPaymentAttemptId())) {
+                        return payment;
+                    }
+                }
+            }
+        }
+
+        return null;
+    }
+
+    @Override
+    public void notifyOfPaymentAttempt(InvoicePayment invoicePayment) {
+        synchronized (monitor) {
+            Invoice invoice = invoices.get(invoicePayment.getInvoiceId());
+            if (invoice != null) {
+                invoice.addPayment(invoicePayment);
+            }
+        }
+    }
+
+    @Override
+    public BigDecimal getAccountBalance(UUID accountId) {
+        BigDecimal balance = BigDecimal.ZERO;
+
+        for (Invoice invoice : get()) {
+            if (accountId.equals(invoice.getAccountId())) {
+                balance = balance.add(invoice.getBalance());
+            }
+        }
+
+        return balance;
+    }
+
+    @Override
+    public List<Invoice> getUnpaidInvoicesByAccountId(UUID accountId, DateTime upToDate) {
+        List<Invoice> unpaidInvoices = new ArrayList<Invoice>();
+
+        for (Invoice invoice : get()) {
+            if (accountId.equals(invoice.getAccountId()) && (invoice.getBalance().compareTo(BigDecimal.ZERO) > 0)) {
+                unpaidInvoices.add(invoice);
+            }
+        }
+
+        return unpaidInvoices;
+    }
+}
diff --git a/invoice/src/test/java/com/ning/billing/invoice/glue/InvoiceModuleWithMocks.java b/invoice/src/test/java/com/ning/billing/invoice/glue/InvoiceModuleWithMocks.java
new file mode 100644
index 0000000..d01301b
--- /dev/null
+++ b/invoice/src/test/java/com/ning/billing/invoice/glue/InvoiceModuleWithMocks.java
@@ -0,0 +1,28 @@
+/*
+ * 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.glue;
+
+import com.ning.billing.invoice.dao.InvoiceDao;
+import com.ning.billing.invoice.dao.MockInvoiceDao;
+
+public class InvoiceModuleWithMocks extends InvoiceModule {
+    @Override
+    protected void installInvoiceDao() {
+        bind(MockInvoiceDao.class).asEagerSingleton();
+        bind(InvoiceDao.class).to(MockInvoiceDao.class);
+    }
+}

payment/pom.xml 100(+97 -3)

diff --git a/payment/pom.xml b/payment/pom.xml
index 3d6356e..5baa4d4 100644
--- a/payment/pom.xml
+++ b/payment/pom.xml
@@ -13,14 +13,108 @@
     <parent>
         <groupId>com.ning.billing</groupId>
         <artifactId>killbill</artifactId>
-        <version>0.1.2-SNAPSHOT</version>
+        <version>0.1.3-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>killbill-payment</artifactId>
     <name>killbill-payment</name>
     <packaging>jar</packaging>
     <dependencies>
+        <dependency>
+            <groupId>com.ning.billing</groupId>
+            <artifactId>killbill-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.ning.billing</groupId>
+            <artifactId>killbill-invoice</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.ning.billing</groupId>
+            <artifactId>killbill-account</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.ning.billing</groupId>
+            <artifactId>killbill-util</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.google.guava</groupId>
+            <artifactId>guava</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>com.google.inject</groupId>
+            <artifactId>guice</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>com.google.inject.extensions</groupId>
+            <artifactId>guice-multibindings</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>joda-time</groupId>
+            <artifactId>joda-time</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>commons-lang</groupId>
+            <artifactId>commons-lang</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>commons-io</groupId>
+            <artifactId>commons-io</artifactId>
+            <version>2.0.1</version>
+        </dependency>
+        <dependency>
+            <groupId>commons-collections</groupId>
+            <artifactId>commons-collections</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>slf4j-api</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.testng</groupId>
+            <artifactId>testng</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>com.jayway.awaitility</groupId>
+            <artifactId>awaitility</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>com.ning.billing</groupId>
+            <artifactId>killbill-util</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>com.ning.billing</groupId>
+            <artifactId>killbill-util</artifactId>
+            <type>test-jar</type>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>com.ning.billing</groupId>
+            <artifactId>killbill-account</artifactId>
+            <type>test-jar</type>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>com.ning.billing</groupId>
+            <artifactId>killbill-invoice</artifactId>
+            <type>test-jar</type>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+             <groupId>com.mysql</groupId>
+             <artifactId>management</artifactId>
+             <scope>test</scope>
+         </dependency>
+         <dependency>
+             <groupId>com.mysql</groupId>
+             <artifactId>management-dbfiles</artifactId>
+             <scope>test</scope>
+         </dependency>
     </dependencies>
-    <build>
-    </build>
 </project>
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
new file mode 100644
index 0000000..1fe63c3
--- /dev/null
+++ b/payment/src/main/java/com/ning/billing/payment/api/DefaultPaymentApi.java
@@ -0,0 +1,198 @@
+/*
+ * 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.payment.api;
+
+import java.math.BigDecimal;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.UUID;
+
+import javax.annotation.Nullable;
+
+import com.ning.billing.invoice.api.InvoicePayment;
+import com.ning.billing.invoice.model.DefaultInvoicePayment;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.inject.Inject;
+import com.ning.billing.account.api.Account;
+import com.ning.billing.account.api.AccountUserApi;
+import com.ning.billing.invoice.api.Invoice;
+import com.ning.billing.invoice.api.InvoicePaymentApi;
+import com.ning.billing.payment.dao.PaymentDao;
+import com.ning.billing.payment.provider.PaymentProviderPlugin;
+import com.ning.billing.payment.provider.PaymentProviderPluginRegistry;
+
+public class DefaultPaymentApi implements PaymentApi {
+    private final PaymentProviderPluginRegistry pluginRegistry;
+    private final AccountUserApi accountUserApi;
+    private final InvoicePaymentApi invoicePaymentApi;
+    private final PaymentDao paymentDao;
+
+    private static final Logger log = LoggerFactory.getLogger(DefaultPaymentApi.class);
+
+    @Inject
+    public DefaultPaymentApi(PaymentProviderPluginRegistry pluginRegistry,
+                             AccountUserApi accountUserApi,
+                             InvoicePaymentApi invoicePaymentApi,
+                             PaymentDao paymentDao) {
+        this.pluginRegistry = pluginRegistry;
+        this.accountUserApi = accountUserApi;
+        this.invoicePaymentApi = invoicePaymentApi;
+        this.paymentDao = paymentDao;
+    }
+
+    @Override
+    public Either<PaymentError, PaymentMethodInfo> getPaymentMethod(@Nullable String accountKey, String paymentMethodId) {
+        final PaymentProviderPlugin plugin = getPaymentProviderPlugin(accountKey);
+        return plugin.getPaymentMethodInfo(paymentMethodId);
+    }
+
+    private PaymentProviderPlugin getPaymentProviderPlugin(String accountKey) {
+        String paymentProviderName = null;
+
+        if (accountKey != null) {
+            final Account account = accountUserApi.getAccountByKey(accountKey);
+            return getPaymentProviderPlugin(account);
+        }
+
+        return pluginRegistry.getPlugin(paymentProviderName);
+    }
+
+    private PaymentProviderPlugin getPaymentProviderPlugin(Account account) {
+        String paymentProviderName = null;
+
+        if (account != null) {
+            paymentProviderName = account.getPaymentProviderName();
+        }
+
+        return pluginRegistry.getPlugin(paymentProviderName);
+    }
+
+    @Override
+    public Either<PaymentError, List<PaymentMethodInfo>> getPaymentMethods(String accountKey) {
+        final PaymentProviderPlugin plugin = getPaymentProviderPlugin(accountKey);
+        return plugin.getPaymentMethods(accountKey);
+    }
+
+    @Override
+    public Either<PaymentError, Void> updatePaymentGateway(String accountKey) {
+        final PaymentProviderPlugin plugin = getPaymentProviderPlugin(accountKey);
+        return plugin.updatePaymentGateway(accountKey);
+    }
+
+    @Override
+    public Either<PaymentError, PaymentProviderAccount> getPaymentProviderAccount(String accountKey) {
+        final PaymentProviderPlugin plugin = getPaymentProviderPlugin(accountKey);
+        return plugin.getPaymentProviderAccount(accountKey);
+    }
+
+    @Override
+    public Either<PaymentError, String> addPaypalPaymentMethod(@Nullable String accountKey, PaypalPaymentMethodInfo paypalPaymentMethod) {
+        final PaymentProviderPlugin plugin = getPaymentProviderPlugin(accountKey);
+        return plugin.addPaypalPaymentMethod(accountKey, paypalPaymentMethod);
+    }
+
+    @Override
+    public Either<PaymentError, Void> deletePaymentMethod(String accountKey, String paymentMethodId) {
+        final PaymentProviderPlugin plugin = getPaymentProviderPlugin(accountKey);
+        return plugin.deletePaypalPaymentMethod(accountKey, paymentMethodId);
+    }
+
+    @Override
+    public Either<PaymentError, PaymentMethodInfo> updatePaymentMethod(String accountKey, PaymentMethodInfo paymentMethodInfo) {
+        final PaymentProviderPlugin plugin = getPaymentProviderPlugin(accountKey);
+        return plugin.updatePaypalPaymentMethod(accountKey, paymentMethodInfo);
+    }
+
+    @Override
+    public List<Either<PaymentError, PaymentInfo>> createPayment(String accountKey, List<String> invoiceIds) {
+        final Account account = accountUserApi.getAccountByKey(accountKey);
+        return createPayment(account, invoiceIds);
+    }
+
+    @Override
+    public List<Either<PaymentError, PaymentInfo>> createPayment(Account account, List<String> invoiceIds) {
+        final PaymentProviderPlugin plugin = getPaymentProviderPlugin(account);
+
+        List<Either<PaymentError, PaymentInfo>> processedPaymentsOrErrors = new ArrayList<Either<PaymentError, PaymentInfo>>(invoiceIds.size());
+
+        for (String invoiceId : invoiceIds) {
+            Invoice invoice = invoicePaymentApi.getInvoice(UUID.fromString(invoiceId));
+
+            if (invoice.getBalance().compareTo(BigDecimal.ZERO) == 0 ) {
+            // TODO: send a notification that invoice was ignored?
+                log.info("Received invoice for payment with outstanding amount of 0 {} ", invoice);
+            }
+            else if (invoiceId.equals(paymentDao.getPaymentAttemptForInvoiceId(invoiceId))) {
+                //TODO: do equals on invoice instead and only reject when invoice is exactly the same?
+                log.info("Duplicate invoice payment event, already received invoice {} ", invoice);
+            }
+            else {
+                PaymentAttempt paymentAttempt = paymentDao.createPaymentAttempt(invoice);
+                Either<PaymentError, PaymentInfo> paymentOrError = plugin.processInvoice(account, invoice);
+                processedPaymentsOrErrors.add(paymentOrError);
+
+                PaymentInfo paymentInfo = null;
+
+                if (paymentOrError.isRight()) {
+                    paymentInfo = paymentOrError.getRight();
+
+                    paymentDao.savePaymentInfo(paymentInfo);
+
+                    if (paymentInfo.getPaymentId() != null) {
+                        paymentDao.updatePaymentAttemptWithPaymentId(paymentAttempt.getPaymentAttemptId(), paymentInfo.getPaymentId());
+                    }
+                }
+
+                invoicePaymentApi.notifyOfPaymentAttempt(new DefaultInvoicePayment(paymentAttempt.getPaymentAttemptId(),
+                                                                                   invoice.getId(),
+                                                                                   paymentAttempt.getPaymentAttemptDate(),
+                                                                                   paymentInfo == null ? null : paymentInfo.getAmount(),
+//                                                                                 paymentInfo.getRefundAmount(), TODO
+                                                                                   paymentInfo == null ? null : invoice.getCurrency()));
+
+            }
+        }
+
+        return processedPaymentsOrErrors;
+    }
+
+    @Override
+    public Either<PaymentError, PaymentProviderAccount> createPaymentProviderAccount(PaymentProviderAccount account) {
+        final PaymentProviderPlugin plugin = getPaymentProviderPlugin((Account)null);
+        return plugin.createPaymentProviderAccount(account);
+    }
+
+    @Override
+    public Either<PaymentError, PaymentProviderAccount> updatePaymentProviderAccount(PaymentProviderAccount account) {
+        //TODO
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public PaymentAttempt getPaymentAttemptForPaymentId(String id) {
+        return paymentDao.getPaymentAttemptForPaymentId(id);
+    }
+
+    @Override
+    public List<Either<PaymentError, PaymentInfo>> createRefund(Account account, List<String> invoiceIds) {
+        //TODO
+        throw new UnsupportedOperationException();
+    }
+
+}
diff --git a/payment/src/main/java/com/ning/billing/payment/dao/DefaultPaymentDao.java b/payment/src/main/java/com/ning/billing/payment/dao/DefaultPaymentDao.java
new file mode 100644
index 0000000..ceba7a7
--- /dev/null
+++ b/payment/src/main/java/com/ning/billing/payment/dao/DefaultPaymentDao.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.payment.dao;
+
+import java.util.UUID;
+
+import org.skife.jdbi.v2.IDBI;
+
+import com.google.inject.Inject;
+import com.ning.billing.invoice.api.Invoice;
+import com.ning.billing.payment.api.PaymentAttempt;
+import com.ning.billing.payment.api.PaymentInfo;
+
+public class DefaultPaymentDao implements PaymentDao {
+    private final PaymentSqlDao sqlDao;
+
+    @Inject
+    public DefaultPaymentDao(IDBI dbi) {
+        this.sqlDao = dbi.onDemand(PaymentSqlDao.class);
+    }
+
+    @Override
+    public PaymentAttempt getPaymentAttemptForPaymentId(String paymentId) {
+        return sqlDao.getPaymentAttemptForPaymentId(paymentId);
+    }
+
+    @Override
+    public PaymentAttempt getPaymentAttemptForInvoiceId(String invoiceId) {
+        return sqlDao.getPaymentAttemptForInvoiceId(invoiceId);
+    }
+
+    @Override
+    public PaymentAttempt createPaymentAttempt(Invoice invoice) {
+        final PaymentAttempt paymentAttempt = new PaymentAttempt(UUID.randomUUID(), invoice);
+
+        sqlDao.insertPaymentAttempt(paymentAttempt);
+        return paymentAttempt;
+    }
+
+    @Override
+    public void savePaymentInfo(PaymentInfo info) {
+        sqlDao.insertPaymentInfo(info);
+    }
+
+    @Override
+    public void updatePaymentAttemptWithPaymentId(UUID paymentAttemptId, String paymentId) {
+        sqlDao.updatePaymentAttemptWithPaymentId(paymentAttemptId.toString(), paymentId);
+    }
+
+}
diff --git a/payment/src/main/java/com/ning/billing/payment/dao/PaymentDao.java b/payment/src/main/java/com/ning/billing/payment/dao/PaymentDao.java
new file mode 100644
index 0000000..d40b264
--- /dev/null
+++ b/payment/src/main/java/com/ning/billing/payment/dao/PaymentDao.java
@@ -0,0 +1,37 @@
+/*
+ * 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.payment.dao;
+
+import java.util.UUID;
+
+import com.ning.billing.invoice.api.Invoice;
+import com.ning.billing.payment.api.PaymentAttempt;
+import com.ning.billing.payment.api.PaymentInfo;
+
+public interface PaymentDao {
+
+    PaymentAttempt createPaymentAttempt(Invoice invoice);
+
+    void savePaymentInfo(PaymentInfo right);
+
+    PaymentAttempt getPaymentAttemptForPaymentId(String paymentId);
+
+    void updatePaymentAttemptWithPaymentId(UUID paymentAttemptId, String paymentId);
+
+    PaymentAttempt getPaymentAttemptForInvoiceId(String invoiceId);
+
+}
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
new file mode 100644
index 0000000..7cd8adb
--- /dev/null
+++ b/payment/src/main/java/com/ning/billing/payment/dao/PaymentSqlDao.java
@@ -0,0 +1,169 @@
+/*
+ * 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.payment.dao;
+
+import java.math.BigDecimal;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Timestamp;
+import java.util.Date;
+import java.util.UUID;
+
+import org.joda.time.DateTime;
+import org.joda.time.DateTimeZone;
+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.SqlQuery;
+import org.skife.jdbi.v2.sqlobject.SqlUpdate;
+import org.skife.jdbi.v2.sqlobject.customizers.Mapper;
+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 com.ning.billing.catalog.api.Currency;
+import com.ning.billing.payment.api.PaymentAttempt;
+import com.ning.billing.payment.api.PaymentInfo;
+
+@ExternalizedSqlViaStringTemplate3()
+public interface PaymentSqlDao extends Transactional<PaymentSqlDao>, CloseMe, Transmogrifier {
+    @SqlUpdate
+    void insertPaymentAttempt(@Bind(binder = PaymentAttemptBinder.class) PaymentAttempt paymentAttempt);
+
+    @SqlQuery
+    @Mapper(PaymentAttemptMapper.class)
+    PaymentAttempt getPaymentAttemptForPaymentId(@Bind("payment_id") String paymentId);
+
+    @SqlQuery
+    @Mapper(PaymentAttemptMapper.class)
+    PaymentAttempt getPaymentAttemptForInvoiceId(@Bind("invoice_id") String invoiceId);
+
+    @SqlUpdate
+    void updatePaymentAttemptWithPaymentId(@Bind("payment_attempt_id") String paymentAttemptId,
+                                           @Bind("payment_id") String payment_id);
+
+    @SqlUpdate
+    void insertPaymentInfo(@Bind(binder = PaymentInfoBinder.class) PaymentInfo paymentInfo);
+
+    public static final class PaymentAttemptBinder implements Binder<Bind, PaymentAttempt> {
+
+        private Date getDate(DateTime dateTime) {
+            return dateTime == null ? null : dateTime.toDate();
+        }
+
+        @Override
+        public void bind(@SuppressWarnings("rawtypes") SQLStatement stmt, Bind bind, PaymentAttempt paymentAttempt) {
+            stmt.bind("payment_attempt_id", paymentAttempt.getPaymentAttemptId().toString());
+            stmt.bind("invoice_id", paymentAttempt.getInvoiceId().toString());
+            stmt.bind("account_id", paymentAttempt.getAccountId().toString());
+            stmt.bind("amount", paymentAttempt.getAmount()); //TODO: suppport partial payments
+            stmt.bind("currency", paymentAttempt.getCurrency().toString());
+            stmt.bind("invoice_dt", getDate(paymentAttempt.getInvoiceDate()));
+            stmt.bind("payment_attempt_dt", getDate(paymentAttempt.getPaymentAttemptDate()));
+            stmt.bind("payment_id", paymentAttempt.getPaymentId());
+            stmt.bind("created_dt", getDate(paymentAttempt.getCreatedDate()));
+            stmt.bind("updated_dt", getDate(paymentAttempt.getUpdatedDate()));
+        }
+    }
+
+    public static class PaymentAttemptMapper implements ResultSetMapper<PaymentAttempt> {
+
+        private DateTime getDate(ResultSet rs, String fieldName) throws SQLException {
+            final Timestamp resultStamp = rs.getTimestamp(fieldName);
+            return rs.wasNull() ? null : new DateTime(resultStamp).toDateTime(DateTimeZone.UTC);
+        }
+
+        @Override
+        public PaymentAttempt map(int index, ResultSet rs, StatementContext ctx) throws SQLException {
+
+            UUID paymentAttemptId = UUID.fromString(rs.getString("payment_attempt_id"));
+            UUID invoiceId = UUID.fromString(rs.getString("invoice_id"));
+            UUID accountId = UUID.fromString(rs.getString("account_id"));
+            BigDecimal amount = rs.getBigDecimal("amount");
+            Currency currency = Currency.valueOf(rs.getString("currency"));
+            DateTime invoiceDate = getDate(rs, "invoice_dt");
+            DateTime paymentAttemptDate = getDate(rs, "payment_attempt_dt");
+            String paymentId = rs.getString("payment_id");
+            DateTime createdDate = getDate(rs, "created_dt");
+            DateTime updatedDate = getDate(rs, "updated_dt");
+
+            return new PaymentAttempt(paymentAttemptId, invoiceId, accountId, amount, currency, invoiceDate, paymentAttemptDate, paymentId, createdDate, updatedDate);
+        }
+    }
+
+    public static final class PaymentInfoBinder implements Binder<Bind, PaymentInfo> {
+
+        private Date getDate(DateTime dateTime) {
+            return dateTime == null ? null : dateTime.toDate();
+        }
+
+        @Override
+        public void bind(@SuppressWarnings("rawtypes") SQLStatement stmt, Bind bind, PaymentInfo paymentInfo) {
+            stmt.bind("payment_id", paymentInfo.getPaymentId().toString());
+            stmt.bind("amount", paymentInfo.getAmount());
+            stmt.bind("refund_amount", paymentInfo.getRefundAmount());
+            stmt.bind("payment_number", paymentInfo.getPaymentNumber());
+            stmt.bind("bank_identification_number", paymentInfo.getBankIdentificationNumber());
+            stmt.bind("status", paymentInfo.getStatus());
+            stmt.bind("payment_type", paymentInfo.getType());
+            stmt.bind("reference_id", paymentInfo.getReferenceId());
+            stmt.bind("effective_dt", getDate(paymentInfo.getEffectiveDate()));
+            stmt.bind("created_dt", getDate(paymentInfo.getCreatedDate()));
+            stmt.bind("updated_dt", getDate(paymentInfo.getUpdatedDate()));
+        }
+    }
+
+    public static class PaymentInfoMapper implements ResultSetMapper<PaymentInfo> {
+
+        private DateTime getDate(ResultSet rs, String fieldName) throws SQLException {
+            final Timestamp resultStamp = rs.getTimestamp(fieldName);
+            return rs.wasNull() ? null : new DateTime(resultStamp).toDateTime(DateTimeZone.UTC);
+        }
+
+        @Override
+        public PaymentInfo map(int index, ResultSet rs, StatementContext ctx) throws SQLException {
+
+            String paymentId = rs.getString("payment_id");
+            BigDecimal amount = rs.getBigDecimal("amount");
+            BigDecimal refundAmount = rs.getBigDecimal("refund_amount");
+            String paymentNumber = rs.getString("payment_number");
+            String bankIdentificationNumber = rs.getString("bank_identification_number");
+            String status = rs.getString("status");
+            String type = rs.getString("payment_type");
+            String referenceId = rs.getString("reference_id");
+            DateTime effectiveDate = getDate(rs, "effective_dt");
+            DateTime createdDate = getDate(rs, "created_dt");
+            DateTime updatedDate = getDate(rs, "updated_dt");
+
+            return new PaymentInfo(paymentId,
+                                   amount,
+                                   refundAmount,
+                                   bankIdentificationNumber,
+                                   paymentNumber,
+                                   status,
+                                   type,
+                                   referenceId,
+                                   effectiveDate,
+                                   createdDate,
+                                   updatedDate);
+        }
+    }
+
+}
diff --git a/payment/src/main/java/com/ning/billing/payment/PaymentAttempt.java b/payment/src/main/java/com/ning/billing/payment/PaymentAttempt.java
new file mode 100644
index 0000000..8d65393
--- /dev/null
+++ b/payment/src/main/java/com/ning/billing/payment/PaymentAttempt.java
@@ -0,0 +1,67 @@
+/*
+ * 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.payment;
+
+import java.math.BigDecimal;
+import java.util.UUID;
+
+import org.joda.time.DateTime;
+import org.joda.time.DateTimeZone;
+
+import com.ning.billing.invoice.api.Invoice;
+
+public class PaymentAttempt {
+    private final UUID paymentAttemptId;
+    private final UUID accountId;
+    private final UUID invoiceId;
+    private final BigDecimal paymentAttemptAmount;
+    private final DateTime paymentAttemptDate;
+
+    public PaymentAttempt(UUID paymentAttemptId, Invoice invoice) {
+        this.paymentAttemptId = paymentAttemptId;
+        this.accountId = invoice.getAccountId();
+        this.invoiceId = invoice.getId();
+        this.paymentAttemptAmount = invoice.getBalance();
+        this.paymentAttemptDate = new DateTime(DateTimeZone.UTC);
+    }
+
+    public UUID getPaymentAttemptId() {
+        return paymentAttemptId;
+    }
+
+    public UUID getAccountId() {
+        return accountId;
+    }
+
+    public UUID getInvoiceId() {
+        return invoiceId;
+    }
+
+    public BigDecimal getPaymentAttemptAmount() {
+        return paymentAttemptAmount;
+    }
+
+        public DateTime getPaymentAttemptDate() {
+            return paymentAttemptDate;
+    }
+
+    @Override
+    public String toString() {
+        return "PaymentAttempt [paymentAttemptId=" + paymentAttemptId + ", accountId=" + accountId + ", invoiceId=" + invoiceId + ", paymentAttemptAmount=" + paymentAttemptAmount + "]";
+    }
+
+}
diff --git a/payment/src/main/java/com/ning/billing/payment/PaymentInfoRequest.java b/payment/src/main/java/com/ning/billing/payment/PaymentInfoRequest.java
new file mode 100644
index 0000000..8ab345f
--- /dev/null
+++ b/payment/src/main/java/com/ning/billing/payment/PaymentInfoRequest.java
@@ -0,0 +1,45 @@
+/*
+ * 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.payment;
+
+import java.util.UUID;
+
+import com.ning.billing.util.eventbus.BusEvent;
+
+public class PaymentInfoRequest implements BusEvent {
+    private final UUID accountId;
+    private final String paymentId;
+
+    public PaymentInfoRequest(UUID accountId, String paymentId) {
+        this.accountId = accountId;
+        this.paymentId = paymentId;
+    }
+
+    public UUID getAccountId() {
+        return accountId;
+    }
+
+    public String getPaymentId() {
+        return paymentId;
+    }
+
+    @Override
+    public String toString() {
+        return "PaymentInfoRequest [accountId=" + accountId + ", paymentId=" + paymentId + "]";
+    }
+
+}
diff --git a/payment/src/main/java/com/ning/billing/payment/provider/PaymentProviderPlugin.java b/payment/src/main/java/com/ning/billing/payment/provider/PaymentProviderPlugin.java
new file mode 100644
index 0000000..4f19eb7
--- /dev/null
+++ b/payment/src/main/java/com/ning/billing/payment/provider/PaymentProviderPlugin.java
@@ -0,0 +1,44 @@
+/*
+ * 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.payment.provider;
+
+import java.util.List;
+
+import com.ning.billing.account.api.Account;
+import com.ning.billing.invoice.api.Invoice;
+import com.ning.billing.payment.api.Either;
+import com.ning.billing.payment.api.PaymentError;
+import com.ning.billing.payment.api.PaymentInfo;
+import com.ning.billing.payment.api.PaymentMethodInfo;
+import com.ning.billing.payment.api.PaymentProviderAccount;
+import com.ning.billing.payment.api.PaypalPaymentMethodInfo;
+
+public interface PaymentProviderPlugin {
+    Either<PaymentError, PaymentInfo> processInvoice(Account account, Invoice invoice);
+    Either<PaymentError, PaymentProviderAccount> createPaymentProviderAccount(PaymentProviderAccount account);
+    Either<PaymentError, String> addPaypalPaymentMethod(String accountId, PaypalPaymentMethodInfo paypalPaymentMethod);
+
+    Either<PaymentError, PaymentInfo> getPaymentInfo(String paymentId);
+    Either<PaymentError, PaymentMethodInfo> getPaymentMethodInfo(String paymentMethodId);
+    Either<PaymentError, PaymentProviderAccount> getPaymentProviderAccount(String accountKey);
+    Either<PaymentError, List<PaymentMethodInfo>> getPaymentMethods(String accountKey);
+
+    Either<PaymentError, Void> updatePaymentGateway(String accountKey);
+    Either<PaymentError, Void> deletePaypalPaymentMethod(String accountKey, String paymentMethodId);
+    Either<PaymentError, PaymentMethodInfo> updatePaypalPaymentMethod(String accountKey, PaymentMethodInfo paymentMethodInfo);
+
+}
diff --git a/payment/src/main/java/com/ning/billing/payment/provider/PaymentProviderPluginRegistry.java b/payment/src/main/java/com/ning/billing/payment/provider/PaymentProviderPluginRegistry.java
new file mode 100644
index 0000000..f7778d8
--- /dev/null
+++ b/payment/src/main/java/com/ning/billing/payment/provider/PaymentProviderPluginRegistry.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.payment.provider;
+
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.apache.commons.lang.StringUtils;
+
+import com.google.inject.Inject;
+import com.ning.billing.payment.setup.PaymentConfig;
+
+public class PaymentProviderPluginRegistry {
+    private final String defaultPlugin;
+    private final Map<String, PaymentProviderPlugin> pluginsByName = new ConcurrentHashMap<String, PaymentProviderPlugin>();
+
+    @Inject
+    public PaymentProviderPluginRegistry(PaymentConfig config) {
+        this.defaultPlugin = config.getDefaultPaymentProvider();
+    }
+
+    public void register(PaymentProviderPlugin plugin, String name) {
+        pluginsByName.put(name.toLowerCase(), plugin);
+    }
+
+    public PaymentProviderPlugin getPlugin(String name) {
+        return pluginsByName.get(StringUtils.defaultIfEmpty(name, defaultPlugin).toLowerCase());
+    }
+}
diff --git a/payment/src/main/java/com/ning/billing/payment/RequestProcessor.java b/payment/src/main/java/com/ning/billing/payment/RequestProcessor.java
new file mode 100644
index 0000000..304e6a1
--- /dev/null
+++ b/payment/src/main/java/com/ning/billing/payment/RequestProcessor.java
@@ -0,0 +1,100 @@
+/*
+ * 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.payment;
+
+import java.util.Arrays;
+import java.util.List;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.eventbus.Subscribe;
+import com.google.inject.Inject;
+import com.ning.billing.account.api.Account;
+import com.ning.billing.account.api.AccountUserApi;
+import com.ning.billing.invoice.api.InvoiceCreationNotification;
+import com.ning.billing.payment.api.Either;
+import com.ning.billing.payment.api.PaymentApi;
+import com.ning.billing.payment.api.PaymentError;
+import com.ning.billing.payment.api.PaymentInfo;
+import com.ning.billing.payment.provider.PaymentProviderPlugin;
+import com.ning.billing.payment.provider.PaymentProviderPluginRegistry;
+import com.ning.billing.util.eventbus.Bus;
+import com.ning.billing.util.eventbus.Bus.EventBusException;
+
+public class RequestProcessor {
+    public static final String PAYMENT_PROVIDER_KEY = "paymentProvider";
+    private final AccountUserApi accountUserApi;
+    private final PaymentApi paymentApi;
+    private final PaymentProviderPluginRegistry pluginRegistry;
+    private final Bus eventBus;
+
+    private static final Logger log = LoggerFactory.getLogger(RequestProcessor.class);
+
+    @Inject
+    public RequestProcessor(AccountUserApi accountUserApi,
+                            PaymentApi paymentApi,
+                            PaymentProviderPluginRegistry pluginRegistry,
+                            Bus eventBus) {
+        this.accountUserApi = accountUserApi;
+        this.paymentApi = paymentApi;
+        this.pluginRegistry = pluginRegistry;
+        this.eventBus = eventBus;
+    }
+
+    @Subscribe
+    public void receiveInvoice(InvoiceCreationNotification event) {
+        log.info("Received invoice creation notification for account {} and invoice {}", event.getAccountId(), event.getInvoiceId());
+        try {
+            final Account account = accountUserApi.getAccountById(event.getAccountId());
+
+            if (account == null) {
+                log.info("could not process invoice payment: could not find a valid account for event {}", event);
+            }
+            else {
+                List<Either<PaymentError, PaymentInfo>> results = paymentApi.createPayment(account, Arrays.asList(event.getInvoiceId().toString()));
+
+                if (results.isEmpty()) {
+                    eventBus.post(new PaymentError("unknown", "No payment processed"));
+                }
+                else {
+                    Either<PaymentError, PaymentInfo> result = results.get(0);
+                    eventBus.post(result.isLeft() ? result.getLeft() : result.getRight());
+                }
+            }
+        }
+        catch (EventBusException ex) {
+            throw new RuntimeException(ex);
+        }
+    }
+
+    @Subscribe
+    public void receivePaymentInfoRequest(PaymentInfoRequest paymentInfoRequest) throws EventBusException {
+        final Account account = accountUserApi.getAccountById(paymentInfoRequest.getAccountId());
+        if (account == null) {
+            log.info("could not process payment info request: could not find a valid account for event {}", paymentInfoRequest);
+        }
+        else {
+            final String paymentProviderName = account.getFieldValue(PAYMENT_PROVIDER_KEY);
+            final PaymentProviderPlugin plugin = pluginRegistry.getPlugin(paymentProviderName);
+
+            Either<PaymentError, PaymentInfo> result = plugin.getPaymentInfo(paymentInfoRequest.getPaymentId());
+
+            eventBus.post(result.isLeft() ? result.getLeft() : result.getRight());
+        }
+    }
+}
diff --git a/payment/src/main/java/com/ning/billing/payment/setup/PaymentConfig.java b/payment/src/main/java/com/ning/billing/payment/setup/PaymentConfig.java
new file mode 100644
index 0000000..cdc5384
--- /dev/null
+++ b/payment/src/main/java/com/ning/billing/payment/setup/PaymentConfig.java
@@ -0,0 +1,26 @@
+/*
+ * 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.payment.setup;
+
+import org.skife.config.Config;
+import org.skife.config.DefaultNull;
+
+public interface PaymentConfig {
+    @Config("killbill.payment.provider.default")
+    @DefaultNull
+    public String getDefaultPaymentProvider();
+}
diff --git a/payment/src/main/java/com/ning/billing/payment/setup/PaymentModule.java b/payment/src/main/java/com/ning/billing/payment/setup/PaymentModule.java
new file mode 100644
index 0000000..3c05c13
--- /dev/null
+++ b/payment/src/main/java/com/ning/billing/payment/setup/PaymentModule.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.payment.setup;
+
+import java.util.Properties;
+
+import org.skife.config.ConfigurationObjectFactory;
+
+import com.google.inject.AbstractModule;
+import com.ning.billing.payment.api.DefaultPaymentApi;
+import com.ning.billing.payment.api.PaymentApi;
+import com.ning.billing.payment.dao.DefaultPaymentDao;
+import com.ning.billing.payment.dao.PaymentDao;
+import com.ning.billing.payment.provider.PaymentProviderPluginRegistry;
+
+public class PaymentModule extends AbstractModule {
+    private final Properties props;
+
+    public PaymentModule() {
+        this.props = System.getProperties();
+    }
+
+    public PaymentModule(Properties props) {
+        this.props = props;
+    }
+
+    protected void installPaymentDao() {
+        bind(PaymentDao.class).to(DefaultPaymentDao.class).asEagerSingleton();
+    }
+
+    protected void installPaymentProviderPlugins(PaymentConfig config) {
+    }
+
+    @Override
+    protected void configure() {
+        final ConfigurationObjectFactory factory = new ConfigurationObjectFactory(props);
+        final PaymentConfig paymentConfig = factory.build(PaymentConfig.class);
+
+        bind(PaymentConfig.class).toInstance(paymentConfig);
+        bind(PaymentProviderPluginRegistry.class).asEagerSingleton();
+        bind(PaymentApi.class).to(DefaultPaymentApi.class).asEagerSingleton();
+        installPaymentProviderPlugins(paymentConfig);
+        installPaymentDao();
+    }
+}
diff --git a/payment/src/main/java/com/ning/billing/payment/util/EventBusFuture.java b/payment/src/main/java/com/ning/billing/payment/util/EventBusFuture.java
new file mode 100644
index 0000000..ae2612a
--- /dev/null
+++ b/payment/src/main/java/com/ning/billing/payment/util/EventBusFuture.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.payment.util;
+
+import javax.annotation.Nullable;
+
+import com.google.common.eventbus.Subscribe;
+import com.google.common.util.concurrent.AbstractFuture;
+import com.ning.billing.util.eventbus.Bus;
+import com.ning.billing.util.eventbus.Bus.EventBusException;
+
+public class EventBusFuture<T, V extends EventBusResponse<T>> extends AbstractFuture<V> {
+    public static <V, W extends EventBusRequest<V>, X extends EventBusResponse<V>> EventBusFuture<V, X> post(final Bus eventBus, final W event) throws EventBusException {
+        final EventBusFuture<V, X> responseFuture = new EventBusFuture<V, X>(eventBus, event.getId());
+
+        eventBus.register(responseFuture);
+        eventBus.post(event);
+        return responseFuture;
+    }
+
+    private final Bus eventBus;
+    private final T requestId;
+
+    private EventBusFuture(Bus eventBus, T requestId) {
+        this.eventBus = eventBus;
+        this.requestId = requestId;
+    }
+
+    @Subscribe
+    public void handleResponse(V response) {
+        if (requestId.equals(response.getRequestId())) {
+            set(response);
+        }
+    }
+
+    @Override
+    public boolean set(@Nullable V value) {
+        boolean result = super.set(value);
+
+        try {
+            eventBus.unregister(this);
+        }
+        catch (EventBusException ex) {
+            throw new RuntimeException(ex);
+        }
+        return result;
+    }
+
+    @Override
+    public boolean setException(Throwable throwable) {
+        boolean result = super.setException(throwable);
+
+        try {
+            eventBus.unregister(this);
+        }
+        catch (EventBusException ex) {
+            throw new RuntimeException(ex);
+        }
+        return result;
+    }
+}
diff --git a/payment/src/main/java/com/ning/billing/payment/util/EventBusRequest.java b/payment/src/main/java/com/ning/billing/payment/util/EventBusRequest.java
new file mode 100644
index 0000000..1ee0cbd
--- /dev/null
+++ b/payment/src/main/java/com/ning/billing/payment/util/EventBusRequest.java
@@ -0,0 +1,23 @@
+/*
+ * 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.payment.util;
+
+import com.ning.billing.util.eventbus.BusEvent;
+
+public interface EventBusRequest<T> extends BusEvent {
+    T getId();
+}
diff --git a/payment/src/main/java/com/ning/billing/payment/util/EventBusResponse.java b/payment/src/main/java/com/ning/billing/payment/util/EventBusResponse.java
new file mode 100644
index 0000000..4b3af2b
--- /dev/null
+++ b/payment/src/main/java/com/ning/billing/payment/util/EventBusResponse.java
@@ -0,0 +1,23 @@
+/*
+ * 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.payment.util;
+
+import com.ning.billing.util.eventbus.BusEvent;
+
+public interface EventBusResponse<T> extends BusEvent {
+    T getRequestId();
+}
diff --git a/payment/src/main/resources/com/ning/billing/payment/dao/PaymentSqlDao.sql.stg b/payment/src/main/resources/com/ning/billing/payment/dao/PaymentSqlDao.sql.stg
new file mode 100644
index 0000000..5156b03
--- /dev/null
+++ b/payment/src/main/resources/com/ning/billing/payment/dao/PaymentSqlDao.sql.stg
@@ -0,0 +1,57 @@
+group PaymentSqlDao;
+
+paymentAttemptFields(prefix) ::= <<
+    <prefix>payment_attempt_id,
+    <prefix>invoice_id,
+    <prefix>account_id,
+    <prefix>amount,
+    <prefix>currency,
+    <prefix>payment_id,
+    <prefix>payment_attempt_dt,
+    <prefix>invoice_dt,
+    <prefix>created_dt,
+    <prefix>updated_dt
+>>
+
+paymentInfoFields(prefix) ::= <<
+    <prefix>payment_id,
+    <prefix>amount,
+    <prefix>refund_amount,
+    <prefix>bank_identification_number,
+    <prefix>payment_number,
+    <prefix>payment_type,
+    <prefix>status,
+    <prefix>reference_id,
+    <prefix>effective_dt,
+    <prefix>created_dt,
+    <prefix>updated_dt
+>>
+
+insertPaymentAttempt() ::= <<
+    INSERT INTO payment_attempts (<paymentAttemptFields()>)
+    VALUES (:payment_attempt_id, :invoice_id, :account_id, :amount, :currency, :payment_id, :payment_attempt_dt, :invoice_dt, :created_dt, :updated_dt);
+>>
+
+getPaymentAttemptForPaymentId() ::= <<
+    SELECT <paymentAttemptFields()>
+      FROM payment_attempts
+     WHERE payment_id = :payment_id
+>>
+
+getPaymentAttemptForInvoiceId() ::= <<
+    SELECT <paymentAttemptFields()>
+      FROM payment_attempts
+     WHERE invoice_id = :invoice_id
+>>
+
+updatePaymentAttemptWithPaymentId() ::= <<
+    UPDATE payment_attempts
+       SET payment_id = :payment_id,
+           updated_dt = NOW()
+     WHERE payment_attempt_id = :payment_attempt_id
+>>
+
+insertPaymentInfo() ::= <<
+    INSERT INTO payments (<paymentInfoFields()>)
+    VALUES (:payment_id, :amount, :refund_amount, :bank_identification_number, :payment_number, :payment_type, :status, :reference_id, :effective_dt, NOW(), NOW());
+>>
diff --git a/payment/src/main/resources/com/ning/billing/payment/ddl.sql b/payment/src/main/resources/com/ning/billing/payment/ddl.sql
new file mode 100644
index 0000000..fd6a1ea
--- /dev/null
+++ b/payment/src/main/resources/com/ning/billing/payment/ddl.sql
@@ -0,0 +1,30 @@
+DROP TABLE IF EXISTS payment_attempts;
+CREATE TABLE payment_attempts (
+      payment_attempt_id char(36) COLLATE utf8_bin NOT NULL,
+      account_id char(36) COLLATE utf8_bin NOT NULL,
+      invoice_id char(36) COLLATE utf8_bin NOT NULL,
+      amount decimal(8,2),
+      currency char(3),
+      payment_attempt_dt datetime NOT NULL,
+      payment_id varchar(36) COLLATE utf8_bin,
+      invoice_dt datetime NOT NULL,
+      created_dt datetime NOT NULL,
+      updated_dt datetime NOT NULL,
+      PRIMARY KEY (payment_attempt_id)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin;
+
+DROP TABLE IF EXISTS payments; 
+CREATE TABLE payments (
+      payment_id varchar(36) COLLATE utf8_bin NOT NULL,
+      amount decimal(8,2),
+      refund_amount decimal(8,2),
+      payment_number varchar(36) COLLATE utf8_bin,
+      bank_identification_number varchar(36) COLLATE utf8_bin,
+      status varchar(20) COLLATE utf8_bin,
+      payment_type varchar(20) COLLATE utf8_bin,
+      reference_id varchar(36) COLLATE utf8_bin,
+      effective_dt datetime,
+      created_dt datetime NOT NULL,
+      updated_dt datetime NOT NULL,
+      PRIMARY KEY (payment_id)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin;
diff --git a/payment/src/test/java/com/ning/billing/payment/api/TestMockPaymentApi.java b/payment/src/test/java/com/ning/billing/payment/api/TestMockPaymentApi.java
new file mode 100644
index 0000000..b9f761a
--- /dev/null
+++ b/payment/src/test/java/com/ning/billing/payment/api/TestMockPaymentApi.java
@@ -0,0 +1,30 @@
+/*
+ * 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.payment.api;
+
+import org.testng.annotations.Guice;
+import org.testng.annotations.Test;
+
+import com.ning.billing.account.glue.AccountModuleWithMocks;
+import com.ning.billing.invoice.glue.InvoiceModuleWithMocks;
+import com.ning.billing.payment.setup.PaymentTestModuleWithMocks;
+
+@Guice(modules = { PaymentTestModuleWithMocks.class, AccountModuleWithMocks.class, InvoiceModuleWithMocks.class })
+@Test(groups = "fast")
+public class TestMockPaymentApi extends TestPaymentApi {
+
+}
diff --git a/payment/src/test/java/com/ning/billing/payment/api/TestPaymentApi.java b/payment/src/test/java/com/ning/billing/payment/api/TestPaymentApi.java
new file mode 100644
index 0000000..1113306
--- /dev/null
+++ b/payment/src/test/java/com/ning/billing/payment/api/TestPaymentApi.java
@@ -0,0 +1,88 @@
+/*
+ * 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.payment.api;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNotNull;
+import static org.testng.Assert.assertTrue;
+
+import java.math.BigDecimal;
+import java.util.Arrays;
+import java.util.List;
+import java.util.UUID;
+
+import org.joda.time.DateTime;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import com.google.inject.Inject;
+import com.ning.billing.account.api.Account;
+import com.ning.billing.catalog.api.Currency;
+import com.ning.billing.invoice.api.Invoice;
+import com.ning.billing.invoice.model.DefaultInvoiceItem;
+import com.ning.billing.payment.TestHelper;
+import com.ning.billing.util.eventbus.Bus;
+import com.ning.billing.util.eventbus.Bus.EventBusException;
+
+public abstract class TestPaymentApi {
+    @Inject
+    private Bus eventBus;
+    @Inject
+    protected PaymentApi paymentApi;
+    @Inject
+    protected TestHelper testHelper;
+
+    @BeforeMethod(alwaysRun = true)
+    public void setUp() throws EventBusException {
+        eventBus.start();
+    }
+
+    @AfterMethod(alwaysRun = true)
+    public void tearDown() throws EventBusException {
+        eventBus.stop();
+    }
+
+//    @Test(groups = "fast")
+    @Test
+    public void testCreatePayment() {
+        final DateTime now = new DateTime();
+        final Account account = testHelper.createTestAccount();
+        final Invoice invoice = testHelper.createTestInvoice(account, now, Currency.USD);
+        final BigDecimal amount = new BigDecimal("10.00");
+        final UUID subscriptionId = UUID.randomUUID();
+
+        invoice.addInvoiceItem(new DefaultInvoiceItem(invoice.getId(),
+                                           subscriptionId,
+                                           now,
+                                           now.plusMonths(1),
+                                           "Test",
+                                           amount,
+                                           new BigDecimal("1.0"),
+                                           Currency.USD));
+
+        List<Either<PaymentError, PaymentInfo>> results = paymentApi.createPayment(account.getExternalKey(), Arrays.asList(invoice.getId().toString()));
+
+        assertEquals(results.size(), 1);
+        assertTrue(results.get(0).isRight());
+
+        PaymentInfo paymentInfo = results.get(0).getRight();
+
+        assertNotNull(paymentInfo.getPaymentId());
+        assertEquals(paymentInfo.getAmount().doubleValue(), amount.doubleValue());
+    }
+}
diff --git a/payment/src/test/java/com/ning/billing/payment/dao/MockPaymentDao.java b/payment/src/test/java/com/ning/billing/payment/dao/MockPaymentDao.java
new file mode 100644
index 0000000..74d8552
--- /dev/null
+++ b/payment/src/test/java/com/ning/billing/payment/dao/MockPaymentDao.java
@@ -0,0 +1,73 @@
+/*
+ * 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.payment.dao;
+
+import java.util.Map;
+import java.util.UUID;
+import java.util.concurrent.ConcurrentHashMap;
+
+import com.ning.billing.invoice.api.Invoice;
+import com.ning.billing.payment.api.PaymentAttempt;
+import com.ning.billing.payment.api.PaymentInfo;
+
+public class MockPaymentDao implements PaymentDao {
+    private final Map<String, PaymentInfo> payments = new ConcurrentHashMap<String, PaymentInfo>();
+    private final Map<UUID, PaymentAttempt> paymentAttempts = new ConcurrentHashMap<UUID, PaymentAttempt>();
+
+    @Override
+    public PaymentAttempt getPaymentAttemptForPaymentId(String paymentId) {
+        for (PaymentAttempt paymentAttempt : paymentAttempts.values()) {
+            if (paymentId.equals(paymentAttempt.getPaymentId())) {
+                return paymentAttempt;
+            }
+        }
+        return null;
+    }
+
+    @Override
+    public PaymentAttempt createPaymentAttempt(Invoice invoice) {
+        PaymentAttempt paymentAttempt = new PaymentAttempt(UUID.randomUUID(), invoice);
+        paymentAttempts.put(paymentAttempt.getPaymentAttemptId(), paymentAttempt);
+        return paymentAttempt;
+    }
+
+    @Override
+    public void savePaymentInfo(PaymentInfo paymentInfo) {
+        payments.put(paymentInfo.getPaymentId(), paymentInfo);
+    }
+
+    @Override
+    public void updatePaymentAttemptWithPaymentId(UUID paymentAttemptId, String paymentId) {
+        PaymentAttempt existingPaymentAttempt = paymentAttempts.get(paymentAttemptId);
+
+        if (existingPaymentAttempt != null) {
+            paymentAttempts.put(existingPaymentAttempt.getPaymentAttemptId(),
+                                existingPaymentAttempt.cloner().setPaymentId(paymentId).build());
+        }
+    }
+
+    @Override
+    public PaymentAttempt getPaymentAttemptForInvoiceId(String invoiceId) {
+        for (PaymentAttempt paymentAttempt : paymentAttempts.values()) {
+            if (invoiceId.equals(paymentAttempt.getInvoiceId())) {
+                return paymentAttempt;
+            }
+        }
+        return null;
+    }
+
+}
diff --git a/payment/src/test/java/com/ning/billing/payment/MockPaymentInfoReceiver.java b/payment/src/test/java/com/ning/billing/payment/MockPaymentInfoReceiver.java
new file mode 100644
index 0000000..cf2494d
--- /dev/null
+++ b/payment/src/test/java/com/ning/billing/payment/MockPaymentInfoReceiver.java
@@ -0,0 +1,53 @@
+/*
+ * 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.payment;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import com.google.common.eventbus.Subscribe;
+import com.ning.billing.payment.api.PaymentError;
+import com.ning.billing.payment.api.PaymentInfo;
+
+public class MockPaymentInfoReceiver {
+    private final List<PaymentInfo> processedPayments = Collections.synchronizedList(new ArrayList<PaymentInfo>());
+    private final List<PaymentError> errors = Collections.synchronizedList(new ArrayList<PaymentError>());
+
+    @Subscribe
+    public void processedPayment(PaymentInfo paymentInfo) {
+        processedPayments.add(paymentInfo);
+    }
+
+    @Subscribe
+    public void processedPaymentError(PaymentError paymentError) {
+        errors.add(paymentError);
+    }
+
+    public List<PaymentInfo> getProcessedPayments() {
+        return new ArrayList<PaymentInfo>(processedPayments);
+    }
+
+    public List<PaymentError> getErrors() {
+        return new ArrayList<PaymentError>(errors);
+    }
+
+    public void clear() {
+        processedPayments.clear();
+        errors.clear();
+    }
+}
\ No newline at end of file
diff --git a/payment/src/test/java/com/ning/billing/payment/provider/MockPaymentProviderPlugin.java b/payment/src/test/java/com/ning/billing/payment/provider/MockPaymentProviderPlugin.java
new file mode 100644
index 0000000..c26f28a
--- /dev/null
+++ b/payment/src/test/java/com/ning/billing/payment/provider/MockPaymentProviderPlugin.java
@@ -0,0 +1,125 @@
+/*
+ * 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.payment.provider;
+
+import java.util.List;
+import java.util.Map;
+import java.util.UUID;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.joda.time.DateTime;
+
+import com.ning.billing.account.api.Account;
+import com.ning.billing.invoice.api.Invoice;
+import com.ning.billing.payment.api.Either;
+import com.ning.billing.payment.api.PaymentError;
+import com.ning.billing.payment.api.PaymentInfo;
+import com.ning.billing.payment.api.PaymentMethodInfo;
+import com.ning.billing.payment.api.PaymentProviderAccount;
+import com.ning.billing.payment.api.PaypalPaymentMethodInfo;
+
+public class MockPaymentProviderPlugin implements PaymentProviderPlugin {
+    private final Map<String, PaymentInfo> payments = new ConcurrentHashMap<String, PaymentInfo>();
+    private final Map<String, PaymentProviderAccount> accounts = new ConcurrentHashMap<String, PaymentProviderAccount>();
+
+    @Override
+    public Either<PaymentError, PaymentInfo> processInvoice(Account account, Invoice invoice) {
+        PaymentInfo payment = new PaymentInfo.Builder().setPaymentId(UUID.randomUUID().toString())
+                                            .setAmount(invoice.getBalance())
+                                            .setStatus("Processed")
+                                            .setBankIdentificationNumber("1234")
+                                            .setCreatedDate(new DateTime())
+                                            .setEffectiveDate(new DateTime())
+                                            .setPaymentNumber("12345")
+                                            .setReferenceId("12345")
+                                            .setType("Electronic")
+                                            .build();
+
+        payments.put(payment.getPaymentId(), payment);
+        return Either.right(payment);
+    }
+
+    @Override
+    public Either<PaymentError, PaymentInfo> getPaymentInfo(String paymentId) {
+        PaymentInfo payment = payments.get(paymentId);
+
+        if (payment == null) {
+            return Either.left(new PaymentError("notfound", "No payment found for id " + paymentId));
+        }
+        else {
+            return Either.right(payment);
+        }
+    }
+
+    @Override
+    public Either<PaymentError, PaymentProviderAccount> createPaymentProviderAccount(PaymentProviderAccount account) {
+        if (account != null) {
+            PaymentProviderAccount paymentProviderAccount = accounts.put(account.getAccountName(),
+                                                                         new PaymentProviderAccount.Builder().setAccountName(account.getAccountName())
+                                                                                                             .setAccountNumber(account.getAccountName())
+                                                                                                             .setId(account.getId())
+                                                                                                             .build());
+
+            return Either.right(paymentProviderAccount);
+        }
+        else {
+            return Either.left(new PaymentError("unknown", "Did not get account to create payment provider account"));
+        }
+    }
+
+    @Override
+    public Either<PaymentError, PaymentMethodInfo> getPaymentMethodInfo(String paymentMethodId) {
+        // TODO
+        return Either.left(new PaymentError("unknown", "Not implemented"));
+    }
+
+    @Override
+    public Either<PaymentError, List<PaymentMethodInfo>> getPaymentMethods(String accountId) {
+        // TODO
+        return Either.left(new PaymentError("unknown", "Not implemented"));
+    }
+
+    @Override
+    public Either<PaymentError, Void> updatePaymentGateway(String accountKey) {
+        // TODO
+        return Either.left(new PaymentError("unknown", "Not implemented"));
+    }
+
+    @Override
+    public Either<PaymentError, PaymentProviderAccount> getPaymentProviderAccount(String accountKey) {
+        // TODO
+        return Either.left(new PaymentError("unknown", "Not implemented"));
+    }
+
+    @Override
+    public Either<PaymentError, String> addPaypalPaymentMethod(String accountId, PaypalPaymentMethodInfo paypalPaymentMethod) {
+        // TODO
+        return Either.left(new PaymentError("unknown", "Not implemented"));
+    }
+
+    @Override
+    public Either<PaymentError, Void> deletePaypalPaymentMethod(String accountKey, String paymentMethodId) {
+        // TODO
+        return Either.left(new PaymentError("unknown", "Not implemented"));
+    }
+
+    @Override
+    public Either<PaymentError, PaymentMethodInfo> updatePaypalPaymentMethod(String accountKey, PaymentMethodInfo paymentMethodInfo) {
+        // TODO
+        return Either.left(new PaymentError("unknown", "Not implemented"));
+    }
+}
diff --git a/payment/src/test/java/com/ning/billing/payment/provider/MockPaymentProviderPluginModule.java b/payment/src/test/java/com/ning/billing/payment/provider/MockPaymentProviderPluginModule.java
new file mode 100644
index 0000000..441fcf8
--- /dev/null
+++ b/payment/src/test/java/com/ning/billing/payment/provider/MockPaymentProviderPluginModule.java
@@ -0,0 +1,36 @@
+/*
+ * 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.payment.provider;
+
+import com.google.inject.AbstractModule;
+import com.google.inject.name.Names;
+
+public class MockPaymentProviderPluginModule extends AbstractModule {
+    private final String instanceName;
+
+    public MockPaymentProviderPluginModule(String instanceName) {
+        this.instanceName = instanceName;
+    }
+
+    @Override
+    protected void configure() {
+        bind(MockPaymentProviderPlugin.class)
+            .annotatedWith(Names.named(instanceName))
+            .toProvider(new MockPaymentProviderPluginProvider(instanceName))
+            .asEagerSingleton();
+    }
+}
diff --git a/payment/src/test/java/com/ning/billing/payment/provider/MockPaymentProviderPluginProvider.java b/payment/src/test/java/com/ning/billing/payment/provider/MockPaymentProviderPluginProvider.java
new file mode 100644
index 0000000..1170007
--- /dev/null
+++ b/payment/src/test/java/com/ning/billing/payment/provider/MockPaymentProviderPluginProvider.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.payment.provider;
+
+import com.google.inject.Inject;
+import com.google.inject.Provider;
+
+public class MockPaymentProviderPluginProvider implements Provider<MockPaymentProviderPlugin> {
+    private PaymentProviderPluginRegistry registry;
+    private final String instanceName;
+
+    public MockPaymentProviderPluginProvider(String instanceName) {
+        this.instanceName = instanceName;
+    }
+
+    @Inject
+    public void setPaymentProviderPluginRegistry(PaymentProviderPluginRegistry registry) {
+        this.registry = registry;
+    }
+
+    @Override
+    public MockPaymentProviderPlugin get() {
+        MockPaymentProviderPlugin plugin = new MockPaymentProviderPlugin();
+
+        registry.register(plugin, instanceName);
+        return plugin;
+    }
+}
diff --git a/payment/src/test/java/com/ning/billing/payment/setup/PaymentModuleWithMocks.java b/payment/src/test/java/com/ning/billing/payment/setup/PaymentModuleWithMocks.java
new file mode 100644
index 0000000..e051440
--- /dev/null
+++ b/payment/src/test/java/com/ning/billing/payment/setup/PaymentModuleWithMocks.java
@@ -0,0 +1,27 @@
+/*
+ * 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.payment.setup;
+
+import com.ning.billing.payment.dao.MockPaymentDao;
+import com.ning.billing.payment.dao.PaymentDao;
+
+public class PaymentModuleWithMocks extends PaymentModule {
+    @Override
+    protected void installPaymentDao() {
+        bind(PaymentDao.class).to(MockPaymentDao.class).asEagerSingleton();
+    }
+}
diff --git a/payment/src/test/java/com/ning/billing/payment/setup/PaymentTestModuleWithEmbeddedDb.java b/payment/src/test/java/com/ning/billing/payment/setup/PaymentTestModuleWithEmbeddedDb.java
new file mode 100644
index 0000000..e8c723b
--- /dev/null
+++ b/payment/src/test/java/com/ning/billing/payment/setup/PaymentTestModuleWithEmbeddedDb.java
@@ -0,0 +1,41 @@
+/*
+ * 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.payment.setup;
+
+import com.ning.billing.util.eventbus.Bus;
+import org.apache.commons.collections.MapUtils;
+
+import com.google.common.collect.ImmutableMap;
+import com.ning.billing.payment.provider.MockPaymentProviderPluginModule;
+import com.ning.billing.util.eventbus.MemoryEventBus;
+
+public class PaymentTestModuleWithEmbeddedDb extends PaymentModule {
+    public PaymentTestModuleWithEmbeddedDb() {
+        super(MapUtils.toProperties(ImmutableMap.of("killbill.payment.provider.default", "my-mock")));
+    }
+
+    @Override
+    protected void installPaymentProviderPlugins(PaymentConfig config) {
+        install(new MockPaymentProviderPluginModule("my-mock"));
+    }
+
+    @Override
+    protected void configure() {
+        super.configure();
+        bind(Bus.class).to(MemoryEventBus.class).asEagerSingleton();
+    }
+}
diff --git a/payment/src/test/java/com/ning/billing/payment/setup/PaymentTestModuleWithMocks.java b/payment/src/test/java/com/ning/billing/payment/setup/PaymentTestModuleWithMocks.java
new file mode 100644
index 0000000..75c045d
--- /dev/null
+++ b/payment/src/test/java/com/ning/billing/payment/setup/PaymentTestModuleWithMocks.java
@@ -0,0 +1,56 @@
+/*
+ * 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.payment.setup;
+
+import org.apache.commons.collections.MapUtils;
+
+import com.google.common.collect.ImmutableMap;
+import com.ning.billing.account.dao.AccountDao;
+import com.ning.billing.account.dao.MockAccountDao;
+import com.ning.billing.invoice.dao.InvoiceDao;
+import com.ning.billing.invoice.dao.MockInvoiceDao;
+import com.ning.billing.payment.dao.MockPaymentDao;
+import com.ning.billing.payment.dao.PaymentDao;
+import com.ning.billing.payment.provider.MockPaymentProviderPluginModule;
+import com.ning.billing.util.eventbus.Bus;
+import com.ning.billing.util.eventbus.MemoryEventBus;
+
+public class PaymentTestModuleWithMocks extends PaymentModule {
+    public PaymentTestModuleWithMocks() {
+        super(MapUtils.toProperties(ImmutableMap.of("killbill.payment.provider.default", "my-mock")));
+    }
+
+    @Override
+    protected void installPaymentDao() {
+        bind(PaymentDao.class).to(MockPaymentDao.class).asEagerSingleton();
+    }
+
+    @Override
+    protected void installPaymentProviderPlugins(PaymentConfig config) {
+        install(new MockPaymentProviderPluginModule("my-mock"));
+    }
+
+    @Override
+    protected void configure() {
+        super.configure();
+        bind(Bus.class).to(MemoryEventBus.class).asEagerSingleton();
+        bind(MockAccountDao.class).asEagerSingleton();
+        bind(AccountDao.class).to(MockAccountDao.class);
+        bind(MockInvoiceDao.class).asEagerSingleton();
+        bind(InvoiceDao.class).to(MockInvoiceDao.class);
+    }
+}
diff --git a/payment/src/test/java/com/ning/billing/payment/TestHelper.java b/payment/src/test/java/com/ning/billing/payment/TestHelper.java
new file mode 100644
index 0000000..2f4a7d1
--- /dev/null
+++ b/payment/src/test/java/com/ning/billing/payment/TestHelper.java
@@ -0,0 +1,89 @@
+/*
+ * 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.payment;
+
+import java.math.BigDecimal;
+import java.util.UUID;
+
+import org.apache.commons.lang.RandomStringUtils;
+import org.joda.time.DateTime;
+import org.joda.time.DateTimeZone;
+
+import com.google.inject.Inject;
+import com.ning.billing.account.api.Account;
+import com.ning.billing.account.api.user.AccountBuilder;
+import com.ning.billing.account.dao.AccountDao;
+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.dao.InvoiceDao;
+import com.ning.billing.invoice.model.DefaultInvoice;
+import com.ning.billing.invoice.model.DefaultInvoiceItem;
+
+public class TestHelper {
+    protected final AccountDao accountDao;
+    protected final InvoiceDao invoiceDao;
+
+    @Inject
+    public TestHelper(AccountDao accountDao, InvoiceDao invoiceDao) {
+        this.accountDao = accountDao;
+        this.invoiceDao = invoiceDao;
+    }
+
+    public Account createTestAccount() {
+        final String name = "First" + RandomStringUtils.random(5) + " " + "Last" + RandomStringUtils.random(5);
+        final Account account = new AccountBuilder(UUID.randomUUID()).name(name)
+                                                                     .firstNameLength(name.length())
+                                                                     .externalKey("12345")
+                                                                     .phone("123-456-7890")
+                                                                     .email("user@example.com")
+                                                                     .currency(Currency.USD)
+                                                                     .billingCycleDay(1)
+                                                                     .build();
+        accountDao.create(account);
+        return account;
+    }
+
+    public Invoice createTestInvoice(Account account,
+                                     DateTime targetDate,
+                                     Currency currency,
+                                     InvoiceItem... items) {
+        Invoice invoice = new DefaultInvoice(UUID.randomUUID(), account.getId(), new DateTime(), targetDate, currency);
+
+        for (InvoiceItem item : items) {
+            invoice.addInvoiceItem(new DefaultInvoiceItem(invoice.getId(),
+                                               item.getSubscriptionId(),
+                                               item.getStartDate(),
+                                               item.getEndDate(),
+                                               item.getDescription(),
+                                               item.getAmount(),
+                                               item.getRate(),
+                                               item.getCurrency()));
+        }
+        invoiceDao.create(invoice);
+        return invoice;
+    }
+
+    public Invoice createTestInvoice(Account account) {
+        final DateTime now = new DateTime(DateTimeZone.UTC);
+        final UUID subscriptionId = UUID.randomUUID();
+        final BigDecimal amount = new BigDecimal("10.00");
+        final InvoiceItem item = new DefaultInvoiceItem(null, subscriptionId, now, now.plusMonths(1), "Test", amount, new BigDecimal("1.0"), Currency.USD);
+
+        return createTestInvoice(account, now, Currency.USD, item);
+    }
+}
diff --git a/payment/src/test/java/com/ning/billing/payment/TestNotifyInvoicePaymentApi.java b/payment/src/test/java/com/ning/billing/payment/TestNotifyInvoicePaymentApi.java
new file mode 100644
index 0000000..ef36ba3
--- /dev/null
+++ b/payment/src/test/java/com/ning/billing/payment/TestNotifyInvoicePaymentApi.java
@@ -0,0 +1,96 @@
+/*
+ * 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.payment;
+
+import static org.testng.Assert.assertNotNull;
+
+import java.util.UUID;
+
+import com.ning.billing.invoice.api.InvoicePayment;
+import org.testng.annotations.AfterMethod;
+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.account.glue.AccountModuleWithMocks;
+import com.ning.billing.invoice.api.Invoice;
+import com.ning.billing.invoice.api.InvoicePaymentApi;
+import com.ning.billing.invoice.glue.InvoiceModuleWithMocks;
+import com.ning.billing.payment.setup.PaymentTestModuleWithMocks;
+import com.ning.billing.util.eventbus.Bus;
+import com.ning.billing.util.eventbus.Bus.EventBusException;
+
+@Test
+@Guice(modules = { PaymentTestModuleWithMocks.class, AccountModuleWithMocks.class, InvoiceModuleWithMocks.class })
+public class TestNotifyInvoicePaymentApi {
+    @Inject
+    private Bus eventBus;
+    @Inject
+    private RequestProcessor invoiceProcessor;
+    @Inject
+    private InvoicePaymentApi invoicePaymentApi;
+    @Inject
+    private TestHelper testHelper;
+
+    @BeforeMethod(alwaysRun = true)
+    public void setUp() throws EventBusException {
+        eventBus.start();
+        eventBus.register(invoiceProcessor);
+    }
+
+    @AfterMethod(alwaysRun = true)
+    public void tearDown() throws EventBusException {
+        eventBus.unregister(invoiceProcessor);
+        eventBus.stop();
+    }
+
+    @Test
+    public void testNotifyPaymentSuccess() {
+        final Account account = testHelper.createTestAccount();
+        final Invoice invoice = testHelper.createTestInvoice(account);
+
+        PaymentAttempt paymentAttempt = new PaymentAttempt(UUID.randomUUID(), invoice);
+
+        invoicePaymentApi.notifyOfPaymentAttempt(invoice.getId(),
+                                     invoice.getBalance(),
+                                     invoice.getCurrency(),
+                                     paymentAttempt.getPaymentAttemptId(),
+                                     paymentAttempt.getPaymentAttemptDate());
+
+        InvoicePayment invoicePayment = invoicePaymentApi.getInvoicePayment(paymentAttempt.getPaymentAttemptId());
+
+        assertNotNull(invoicePayment);
+    }
+
+    @Test
+    public void testNotifyPaymentFailure() {
+        final Account account = testHelper.createTestAccount();
+        final Invoice invoice = testHelper.createTestInvoice(account);
+
+        PaymentAttempt paymentAttempt = new PaymentAttempt(UUID.randomUUID(), invoice);
+        invoicePaymentApi.notifyOfPaymentAttempt(invoice.getId(),
+                                                 paymentAttempt.getPaymentAttemptId(),
+                                                 paymentAttempt.getPaymentAttemptDate());
+
+        InvoicePayment invoicePayment = invoicePaymentApi.getInvoicePayment(paymentAttempt.getPaymentAttemptId());
+
+        assertNotNull(invoicePayment);
+    }
+
+}
diff --git a/payment/src/test/java/com/ning/billing/payment/TestPaymentInvoiceIntegration.java b/payment/src/test/java/com/ning/billing/payment/TestPaymentInvoiceIntegration.java
new file mode 100644
index 0000000..9f70017
--- /dev/null
+++ b/payment/src/test/java/com/ning/billing/payment/TestPaymentInvoiceIntegration.java
@@ -0,0 +1,152 @@
+/*
+ * 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.payment;
+
+import static com.jayway.awaitility.Awaitility.await;
+import static java.util.concurrent.TimeUnit.MINUTES;
+import static org.testng.Assert.assertFalse;
+import static org.testng.Assert.assertTrue;
+
+import java.io.IOException;
+import java.math.BigDecimal;
+import java.util.List;
+import java.util.concurrent.Callable;
+
+import org.apache.commons.io.IOUtils;
+import org.skife.jdbi.v2.IDBI;
+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.Test;
+
+import com.google.inject.AbstractModule;
+import com.google.inject.Guice;
+import com.google.inject.Inject;
+import com.google.inject.Injector;
+import com.ning.billing.account.api.Account;
+import com.ning.billing.account.glue.AccountModule;
+import com.ning.billing.dbi.MysqlTestingHelper;
+import com.ning.billing.invoice.api.Invoice;
+import com.ning.billing.invoice.api.InvoicePaymentApi;
+import com.ning.billing.invoice.glue.InvoiceModule;
+import com.ning.billing.payment.api.PaymentApi;
+import com.ning.billing.payment.api.PaymentAttempt;
+import com.ning.billing.payment.api.PaymentError;
+import com.ning.billing.payment.api.PaymentInfo;
+import com.ning.billing.payment.setup.PaymentTestModuleWithEmbeddedDb;
+import com.ning.billing.util.eventbus.Bus;
+import com.ning.billing.util.eventbus.Bus.EventBusException;
+
+public class TestPaymentInvoiceIntegration {
+    // create payment for received invoice and save it -- positive and negative
+    // check that notification for payment attempt is created
+    // check that invoice-payment is saved
+    @Inject
+    private Bus eventBus;
+    @Inject
+    private RequestProcessor invoiceProcessor;
+    @Inject
+    private InvoicePaymentApi invoicePaymentApi;
+    @Inject
+    private PaymentApi paymentApi;
+    @Inject
+    private TestHelper testHelper;
+
+    private MockPaymentInfoReceiver paymentInfoReceiver;
+
+    private IDBI dbi;
+    private MysqlTestingHelper helper;
+
+    @BeforeClass(alwaysRun = true)
+    public void startMysql() throws IOException {
+        final String accountddl = IOUtils.toString(MysqlTestingHelper.class.getResourceAsStream("/com/ning/billing/account/ddl.sql"));
+        final String utilddl = IOUtils.toString(MysqlTestingHelper.class.getResourceAsStream("/com/ning/billing/util/ddl.sql"));
+        final String invoiceddl = IOUtils.toString(MysqlTestingHelper.class.getResourceAsStream("/com/ning/billing/invoice/ddl.sql"));
+        final String paymentddl = IOUtils.toString(MysqlTestingHelper.class.getResourceAsStream("/com/ning/billing/payment/ddl.sql"));
+
+        helper = new MysqlTestingHelper();
+        helper.startMysql();
+        helper.initDb(accountddl + "\n" + invoiceddl + "\n" + utilddl + "\n" + paymentddl);
+        dbi = helper.getDBI();
+    }
+
+    @AfterClass(alwaysRun = true)
+    public void stopMysql() {
+        helper.stopMysql();
+    }
+
+    @BeforeMethod(alwaysRun = true)
+    public void setUp() throws EventBusException {
+        Injector injector = Guice.createInjector(new PaymentTestModuleWithEmbeddedDb(),
+                                                 new AccountModule(),
+                                                 new InvoiceModule(),
+                                                 new AbstractModule() {
+                                                    @Override
+                                                    protected void configure() {
+                                                        bind(IDBI.class).toInstance(dbi);
+                                                    }
+                                                });
+        injector.injectMembers(this);
+
+        paymentInfoReceiver = new MockPaymentInfoReceiver();
+
+        eventBus.start();
+        eventBus.register(invoiceProcessor);
+        eventBus.register(paymentInfoReceiver);
+    }
+
+    @AfterMethod(alwaysRun = true)
+    public void tearDown() throws EventBusException {
+        eventBus.unregister(invoiceProcessor);
+        eventBus.unregister(paymentInfoReceiver);
+        eventBus.stop();
+    }
+
+    @Test
+    public void testInvoiceIntegration() throws Exception {
+        final Account account = testHelper.createTestAccount();
+        final Invoice invoice = testHelper.createTestInvoice(account);
+
+        await().atMost(1, MINUTES).until(new Callable<Boolean>() {
+            @Override
+            public Boolean call() throws Exception {
+                List<PaymentInfo> processedPayments = paymentInfoReceiver.getProcessedPayments();
+                List<PaymentError> errors = paymentInfoReceiver.getErrors();
+
+                return processedPayments.size() == 1 || errors.size() == 1;
+            }
+        });
+
+        assertFalse(paymentInfoReceiver.getProcessedPayments().isEmpty());
+        assertTrue(paymentInfoReceiver.getErrors().isEmpty());
+
+        List<PaymentInfo> payments = paymentInfoReceiver.getProcessedPayments();
+        PaymentAttempt paymentAttempt = paymentApi.getPaymentAttemptForPaymentId(payments.get(0).getPaymentId());
+        Assert.assertNotNull(paymentAttempt);
+
+        Invoice invoiceForPayment = invoicePaymentApi.getInvoiceForPaymentAttemptId(paymentAttempt.getPaymentAttemptId());
+
+        Assert.assertNotNull(invoiceForPayment);
+        Assert.assertEquals(invoiceForPayment.getId(), invoice.getId());
+        Assert.assertEquals(invoiceForPayment.getAccountId(), account.getId());
+        Assert.assertTrue(invoiceForPayment.getLastPaymentAttempt().isEqual(paymentAttempt.getPaymentAttemptDate()));
+        Assert.assertEquals(invoiceForPayment.getBalance().floatValue(), new BigDecimal("0").floatValue());
+        Assert.assertEquals(invoiceForPayment.getAmountPaid().floatValue(), invoice.getBalance().floatValue());
+    }
+}
diff --git a/payment/src/test/java/com/ning/billing/payment/TestPaymentProvider.java b/payment/src/test/java/com/ning/billing/payment/TestPaymentProvider.java
new file mode 100644
index 0000000..2fb040e
--- /dev/null
+++ b/payment/src/test/java/com/ning/billing/payment/TestPaymentProvider.java
@@ -0,0 +1,108 @@
+/*
+ * 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.payment;
+
+import static com.jayway.awaitility.Awaitility.await;
+import static java.util.concurrent.TimeUnit.MINUTES;
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertFalse;
+import static org.testng.Assert.assertTrue;
+
+import java.util.List;
+import java.util.concurrent.Callable;
+
+import org.testng.annotations.AfterMethod;
+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.account.glue.AccountModuleWithMocks;
+import com.ning.billing.invoice.glue.InvoiceModuleWithMocks;
+import com.ning.billing.payment.api.PaymentError;
+import com.ning.billing.payment.api.PaymentInfo;
+import com.ning.billing.payment.setup.PaymentTestModuleWithMocks;
+import com.ning.billing.util.eventbus.Bus;
+import com.ning.billing.util.eventbus.Bus.EventBusException;
+
+@Guice(modules = { PaymentTestModuleWithMocks.class, AccountModuleWithMocks.class, InvoiceModuleWithMocks.class })
+public class TestPaymentProvider {
+    @Inject
+    private Bus eventBus;
+    @Inject
+    private RequestProcessor invoiceProcessor;
+    @Inject
+    private TestHelper testHelper;
+
+    private MockPaymentInfoReceiver paymentInfoReceiver;
+
+    @BeforeMethod(alwaysRun = true)
+    public void setUp() throws EventBusException {
+        paymentInfoReceiver = new MockPaymentInfoReceiver();
+
+        eventBus.start();
+        eventBus.register(invoiceProcessor);
+        eventBus.register(paymentInfoReceiver);
+    }
+
+    @AfterMethod(alwaysRun = true)
+    public void tearDown() throws EventBusException {
+        eventBus.unregister(invoiceProcessor);
+        eventBus.unregister(paymentInfoReceiver);
+        eventBus.stop();
+    }
+
+    @Test
+    public void testSimpleInvoice() throws Exception {
+        final Account account = testHelper.createTestAccount();
+
+        testHelper.createTestInvoice(account);
+
+        await().atMost(1, MINUTES).until(new Callable<Boolean>() {
+            @Override
+            public Boolean call() throws Exception {
+                List<PaymentInfo> processedPayments = paymentInfoReceiver.getProcessedPayments();
+                List<PaymentError> errors = paymentInfoReceiver.getErrors();
+
+                return processedPayments.size() == 1 || errors.size() == 1;
+            }
+        });
+
+        assertFalse(paymentInfoReceiver.getProcessedPayments().isEmpty());
+        assertTrue(paymentInfoReceiver.getErrors().isEmpty());
+
+        final PaymentInfo paymentInfo = paymentInfoReceiver.getProcessedPayments().get(0);
+        final PaymentInfoRequest paymentInfoRequest = new PaymentInfoRequest(account.getId(), paymentInfo.getPaymentId());
+
+        paymentInfoReceiver.clear();
+        eventBus.post(paymentInfoRequest);
+        await().atMost(5, MINUTES).until(new Callable<Boolean>() {
+            @Override
+            public Boolean call() throws Exception {
+                List<PaymentInfo> processedPayments = paymentInfoReceiver.getProcessedPayments();
+                List<PaymentError> errors = paymentInfoReceiver.getErrors();
+
+                return processedPayments.size() == 1 || errors.size() == 1;
+            }
+        });
+
+        assertFalse(paymentInfoReceiver.getProcessedPayments().isEmpty());
+        assertTrue(paymentInfoReceiver.getErrors().isEmpty());
+        assertEquals(paymentInfoReceiver.getProcessedPayments().get(0), paymentInfo);
+    }
+}
diff --git a/payment/src/test/java/com/ning/billing/payment/util/TestSyncWaitOnEventBus.java b/payment/src/test/java/com/ning/billing/payment/util/TestSyncWaitOnEventBus.java
new file mode 100644
index 0000000..63f353d
--- /dev/null
+++ b/payment/src/test/java/com/ning/billing/payment/util/TestSyncWaitOnEventBus.java
@@ -0,0 +1,102 @@
+/*
+ * 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.payment.util;
+
+import static org.testng.Assert.assertEquals;
+
+import java.util.UUID;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import com.google.common.eventbus.Subscribe;
+import com.ning.billing.util.eventbus.Bus;
+import com.ning.billing.util.eventbus.MemoryEventBus;
+
+@Test
+public class TestSyncWaitOnEventBus {
+    private static final class TestEvent implements EventBusRequest<UUID> {
+        private final UUID id;
+        private final String msg;
+
+        public TestEvent(UUID id, String msg) {
+            this.id = id;
+            this.msg = msg;
+        }
+
+        @Override
+        public UUID getId() {
+            return id;
+        }
+
+        public String getMsg() {
+            return msg;
+        }
+    }
+
+    private static final class TestResponse implements EventBusResponse<UUID> {
+        private final UUID id;
+        private final String msg;
+
+        public TestResponse(UUID id, String msg) {
+            this.id = id;
+            this.msg = msg;
+        }
+
+        @Override
+        public UUID getRequestId() {
+            return id;
+        }
+
+        public String getMsg() {
+            return msg;
+        }
+    }
+
+    private Bus eventBus;
+
+    @BeforeMethod(alwaysRun = true)
+    public void setUp() throws Exception {
+        eventBus = new MemoryEventBus();
+        eventBus.start();
+        eventBus.register(new Object() {
+            @Subscribe
+            public void handleEvent(TestEvent event) throws Exception {
+                Thread.sleep(100);
+                eventBus.post(new TestResponse(event.getId(), event.getMsg()));
+            }
+        });
+    }
+
+    @AfterMethod(alwaysRun = true)
+    public void tearDown() {
+        eventBus.stop();
+    }
+
+    public void test() throws Exception {
+        final TestEvent event = new TestEvent(UUID.randomUUID(), "Hello World!");
+
+        Future<TestResponse> future = EventBusFuture.post(eventBus, event);
+        TestResponse response = future.get(1, TimeUnit.SECONDS);
+
+        assertEquals(response.getRequestId(), event.getId());
+        assertEquals(response.getMsg(), event.getMsg());
+    }
+}
diff --git a/payment/src/test/resources/log4j.xml b/payment/src/test/resources/log4j.xml
new file mode 100644
index 0000000..82b5a26
--- /dev/null
+++ b/payment/src/test/resources/log4j.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!--
+  ~ Copyright 2010 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.
+  -->
+<!DOCTYPE log4j:configuration SYSTEM "http://logging.apache.org/log4j/1.2/apidocs/org/apache/log4j/xml/doc-files/log4j.dtd">
+<log4j:configuration debug="false"
+                     xmlns:log4j='http://jakarta.apache.org/log4j/'>
+    <appender name="CONSOLE" class="org.apache.log4j.ConsoleAppender">
+        <layout class="org.apache.log4j.PatternLayout">
+            <param name="ConversionPattern" value="%p	%d{ISO8601}	%t	%c	%m%n"/>
+        </layout>
+    </appender>
+
+    <root>
+        <level value="info"/>
+        <appender-ref ref="CONSOLE"/>
+    </root>
+</log4j:configuration>

pom.xml 79(+66 -13)

diff --git a/pom.xml b/pom.xml
index fb37e6b..eb22569 100644
--- a/pom.xml
+++ b/pom.xml
@@ -17,7 +17,7 @@
     <groupId>com.ning.billing</groupId>
     <artifactId>killbill</artifactId>
     <packaging>pom</packaging>
-    <version>0.1.2-SNAPSHOT</version>
+    <version>0.1.3-SNAPSHOT</version>
     <name>killbill</name>
     <description>Library for managing recurring subscriptions and the associated billing</description>
     <url>http://github.com/ning/killbill</url>
@@ -29,7 +29,7 @@
         </license>
     </licenses>
     <scm>
-        <connection>scm:git:git://github.com/stephane/killbill.git</connection>
+        <connection>scm:git:git://github.com/ning/killbill.git</connection>
         <developerConnection>scm:git:git@github.com:ning/killbill.git</developerConnection>
         <url>http://github.com/ning/killbill/tree/master</url>
     </scm>
@@ -56,6 +56,12 @@
             </dependency>
             <dependency>
                 <groupId>com.ning.billing</groupId>
+                <artifactId>killbill-api</artifactId>
+                <version>${project.version}</version>
+                <type>test-jar</type>
+            </dependency>
+            <dependency>
+                <groupId>com.ning.billing</groupId>
                 <artifactId>killbill-account</artifactId>
                 <version>${project.version}</version>
             </dependency>
@@ -64,7 +70,6 @@
                 <artifactId>killbill-account</artifactId>
                 <version>${project.version}</version>
                 <type>test-jar</type>
-                <scope>test</scope>
             </dependency>
             <dependency>
                 <groupId>com.ning.billing</groupId>
@@ -73,8 +78,9 @@
             </dependency>
             <dependency>
                 <groupId>com.ning.billing</groupId>
-                <artifactId>killbill-invoice</artifactId>
+                <artifactId>killbill-entitlement</artifactId>
                 <version>${project.version}</version>
+                <type>test-jar</type>
             </dependency>
             <dependency>
                 <groupId>com.ning.billing</groupId>
@@ -86,14 +92,17 @@
                 <artifactId>killbill-catalog</artifactId>
                 <version>${project.version}</version>
                 <type>test-jar</type>
-                <scope>test</scope>
             </dependency>
             <dependency>
                 <groupId>com.ning.billing</groupId>
-                <artifactId>killbill-util</artifactId>
+                <artifactId>killbill-invoice</artifactId>
+                <version>${project.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>com.ning.billing</groupId>
+                <artifactId>killbill-invoice</artifactId>
                 <version>${project.version}</version>
                 <type>test-jar</type>
-                <scope>test</scope>
             </dependency>
             <dependency>
                 <groupId>com.ning.billing</groupId>
@@ -101,19 +110,25 @@
                 <version>${project.version}</version>
             </dependency>
             <dependency>
+                <groupId>com.ning.billing</groupId>
+                <artifactId>killbill-util</artifactId>
+                <version>${project.version}</version>
+                <type>test-jar</type>
+            </dependency>
+            <dependency>
                 <groupId>org.codehaus.jackson</groupId>
                 <artifactId>jackson-core-asl</artifactId>
-                <version>1.9.0</version>
+                <version>1.9.2</version>
             </dependency>
             <dependency>
                 <groupId>org.codehaus.jackson</groupId>
                 <artifactId>jackson-jaxrs</artifactId>
-                <version>1.9.0</version>
+                <version>1.9.2</version>
             </dependency>
             <dependency>
                 <groupId>org.codehaus.jackson</groupId>
                 <artifactId>jackson-mapper-asl</artifactId>
-                <version>1.9.0</version>
+                <version>1.9.2</version>
             </dependency>
             <dependency>
                 <groupId>com.jolbox</groupId>
@@ -139,9 +154,15 @@
                 <scope>provided</scope>
             </dependency>
             <dependency>
+                <groupId>com.google.inject.extensions</groupId>
+                <artifactId>guice-multibindings</artifactId>
+                <version>3.0</version>
+                <scope>provided</scope>
+            </dependency>
+            <dependency>
                 <groupId>com.mogwee</groupId>
                 <artifactId>mogwee-executors</artifactId>
-                <version>1.1.0</version>
+                <version>1.2.0</version>
             </dependency>
             <dependency>
                 <groupId>com.mysql</groupId>
@@ -181,6 +202,11 @@
                 <version>2.5</version>
             </dependency>
             <dependency>
+                <groupId>commons-collections</groupId>
+                <artifactId>commons-collections</artifactId>
+                <version>3.2.1</version>
+            </dependency>
+            <dependency>
                 <groupId>joda-time</groupId>
                 <artifactId>joda-time</artifactId>
                 <version>2.0</version>
@@ -236,10 +262,10 @@
             <dependency>
                 <groupId>org.testng</groupId>
                 <artifactId>testng</artifactId>
-                <version>6.0</version>
+                <version>6.3.1</version>
                 <scope>test</scope>
             </dependency>
-             <dependency>
+            <dependency>
                 <groupId>com.jayway.awaitility</groupId>
                 <artifactId>awaitility</artifactId>
                 <version>1.3.3</version>
@@ -295,6 +321,18 @@
                 </configuration>
             </plugin>
             <plugin>
+              <groupId>org.apache.maven.plugins</groupId>
+              <artifactId>maven-jar-plugin</artifactId>
+              <version>2.2</version>
+              <executions>
+                <execution>
+                  <goals>
+                    <goal>test-jar</goal>
+                  </goals>
+               </execution>
+              </executions>
+            </plugin>
+            <plugin>
                 <groupId>org.apache.maven.plugins</groupId>
                 <artifactId>maven-dependency-plugin</artifactId>
                 <version>2.3</version>
@@ -403,6 +441,21 @@
                     <attachClasses>true</attachClasses>
                 </configuration>
             </plugin>
+            <plugin>
+              <groupId>org.apache.maven.plugins</groupId>
+              <artifactId>maven-source-plugin</artifactId>
+              <version>2.1.2</version>
+              <executions>
+                <execution>
+                  <id>attach-sources</id>
+                  <phase>verify</phase>
+                  <goals>
+                    <goal>jar</goal>
+                    <goal>test-jar</goal>
+                  </goals>
+                </execution>
+              </executions>
+            </plugin>
         </plugins>
     </build>
     <profiles>

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

diff --git a/util/pom.xml b/util/pom.xml
index 74d243c..06b2135 100644
--- a/util/pom.xml
+++ b/util/pom.xml
@@ -13,7 +13,7 @@
     <parent>
         <groupId>com.ning.billing</groupId>
         <artifactId>killbill</artifactId>
-        <version>0.1.2-SNAPSHOT</version>
+        <version>0.1.3-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>killbill-util</artifactId>
diff --git a/util/src/test/java/com/ning/billing/dbi/MysqlTestingHelper.java b/util/src/test/java/com/ning/billing/dbi/MysqlTestingHelper.java
index 6db65a4..1e949a2 100644
--- a/util/src/test/java/com/ning/billing/dbi/MysqlTestingHelper.java
+++ b/util/src/test/java/com/ning/billing/dbi/MysqlTestingHelper.java
@@ -16,8 +16,12 @@
 
 package com.ning.billing.dbi;
 
-import com.mysql.management.MysqldResource;
-import com.mysql.management.MysqldResourceI;
+import java.io.File;
+import java.io.IOException;
+import java.net.ServerSocket;
+import java.util.HashMap;
+import java.util.Map;
+
 import org.apache.commons.io.FileUtils;
 import org.skife.jdbi.v2.DBI;
 import org.skife.jdbi.v2.Handle;
@@ -27,11 +31,8 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.testng.Assert;
 
-import java.io.File;
-import java.io.IOException;
-import java.net.ServerSocket;
-import java.util.HashMap;
-import java.util.Map;
+import com.mysql.management.MysqldResource;
+import com.mysql.management.MysqldResourceI;
 
 /**
  * Utility class to embed MySQL for testing purposes
@@ -73,7 +74,7 @@ public class MysqlTestingHelper
         dbOpts.put(MysqldResourceI.PORT, Integer.toString(port));
         dbOpts.put(MysqldResourceI.INITIALIZE_USER, "true");
         dbOpts.put(MysqldResourceI.INITIALIZE_USER_NAME, USERNAME);
-        dbOpts.put(MysqldResourceI.INITIALIZE_PASSWORD, PASSWORD);
+        dbOpts.put("default-time-zone", "+00:00");
 
         mysqldResource.start("test-mysqld-thread", dbOpts);
         if (!mysqldResource.isRunning()) {
diff --git a/util/src/test/java/com/ning/billing/util/eventbus/TestEventBus.java b/util/src/test/java/com/ning/billing/util/eventbus/TestEventBus.java
index 0091ab6..54d0163 100644
--- a/util/src/test/java/com/ning/billing/util/eventbus/TestEventBus.java
+++ b/util/src/test/java/com/ning/billing/util/eventbus/TestEventBus.java
@@ -52,6 +52,16 @@ public class TestEventBus {
         }
     }
 
+    public static final class MyOtherEvent implements BusEvent {
+        String name;
+        Long value;
+
+        public MyOtherEvent(String name, Long value) {
+            this.name = name;
+            this.value = value;
+        }
+    }
+
     public static class MyEventHandler {
 
         private final int expectedEvents;
@@ -87,8 +97,8 @@ public class TestEventBus {
         }
     }
 
-    @Test()
-    public void test() {
+    @Test
+    public void testSimple() {
         try {
 
             int nbEvents = 127;
@@ -104,6 +114,25 @@ public class TestEventBus {
         } catch (Exception e) {
             Assert.fail("",e);
         }
+    }
+
+    @Test
+    public void testDifferentType() {
+        try {
+
+            MyEventHandler handler = new MyEventHandler(1);
+            eventBus.register(handler);
+
+            for (int i = 0; i < 10; i++) {
+                eventBus.post(new MyOtherEvent("my-other-event", (long) i));
+            }
+            eventBus.post(new MyEvent("my-event", 11l));
+
+            boolean completed = handler.waitForCompletion(3000);
+            Assert.assertEquals(completed, true);
+        } catch (Exception e) {
+            Assert.fail("",e);
+        }
 
     }
 }