killbill-aplcache
Changes
account/pom.xml 2(+1 -1)
analytics/pom.xml 2(+1 -1)
api/pom.xml 10(+9 -1)
beatrix/pom.xml 2(+1 -1)
catalog/pom.xml 2(+1 -1)
entitlement/pom.xml 7(+6 -1)
invoice/pom.xml 3(+1 -2)
invoice/src/main/java/com/ning/billing/invoice/api/invoice/DefaultInvoicePaymentApi.java 51(+38 -13)
invoice/src/main/java/com/ning/billing/invoice/api/user/DefaultInvoiceCreationNotification.java 8(+8 -0)
payment/pom.xml 100(+97 -3)
payment/src/main/java/com/ning/billing/payment/provider/PaymentProviderPluginRegistry.java 43(+43 -0)
payment/src/test/java/com/ning/billing/payment/provider/MockPaymentProviderPluginModule.java 36(+36 -0)
payment/src/test/java/com/ning/billing/payment/provider/MockPaymentProviderPluginProvider.java 42(+42 -0)
payment/src/test/java/com/ning/billing/payment/setup/PaymentTestModuleWithEmbeddedDb.java 41(+41 -0)
payment/src/test/resources/log4j.xml 30(+30 -0)
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);
+ }
+}
analytics/pom.xml 2(+1 -1)
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>
entitlement/pom.xml 7(+6 -1)
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());
+ }
+}
payment/src/test/resources/log4j.xml 30(+30 -0)
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);
+ }
}
}