Details
diff --git a/api/src/main/java/com/ning/billing/payment/api/PaymentApi.java b/api/src/main/java/com/ning/billing/payment/api/PaymentApi.java
index fb07745..c37ee01 100644
--- a/api/src/main/java/com/ning/billing/payment/api/PaymentApi.java
+++ b/api/src/main/java/com/ning/billing/payment/api/PaymentApi.java
@@ -20,6 +20,8 @@ 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);
@@ -34,6 +36,7 @@ public interface PaymentApi {
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);
Either<PaymentError, PaymentProviderAccount> getPaymentProviderAccount(String accountKey);
@@ -41,5 +44,6 @@ public interface PaymentApi {
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
index c4b6bfd..58016e0 100644
--- a/api/src/main/java/com/ning/billing/payment/api/PaymentAttempt.java
+++ b/api/src/main/java/com/ning/billing/payment/api/PaymentAttempt.java
@@ -25,6 +25,7 @@ import com.ning.billing.invoice.api.Invoice;
public class PaymentAttempt {
private final UUID paymentAttemptId;
+ private final String paymentId;
private final DateTime paymentAttemptDate;
private final Invoice invoice;
@@ -32,12 +33,28 @@ public class PaymentAttempt {
this.paymentAttemptId = paymentAttemptId;
this.paymentAttemptDate = new DateTime(DateTimeZone.UTC);
this.invoice = invoice;
+ this.paymentId = null;
+ }
+
+ public PaymentAttempt(UUID paymentAttemptId, Invoice invoice, String paymentId) {
+ this(paymentAttemptId, invoice, paymentId, null);
+ }
+
+ public PaymentAttempt(UUID paymentAttemptId, Invoice invoice, String paymentId, DateTime paymentAttemptDate) {
+ this.paymentAttemptId = paymentAttemptId;
+ this.paymentAttemptDate = paymentAttemptDate == null ? new DateTime(DateTimeZone.UTC) : paymentAttemptDate;
+ this.invoice = invoice;
+ this.paymentId = paymentId;
}
public UUID getPaymentAttemptId() {
return paymentAttemptId;
}
+ public String getPaymentId() {
+ return paymentId;
+ }
+
public DateTime getPaymentAttemptDate() {
return paymentAttemptDate;
}
@@ -48,7 +65,7 @@ public class PaymentAttempt {
@Override
public String toString() {
- return "PaymentAttempt [paymentAttemptId=" + paymentAttemptId + ", paymentAttemptDate=" + paymentAttemptDate + ", invoice=" + invoice + "]";
+ return "PaymentAttempt [paymentAttemptId=" + paymentAttemptId + ", paymentId=" + paymentId + ", paymentAttemptDate=" + paymentAttemptDate + ", invoice=" + invoice + "]";
}
}
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
index 897ba89..1ccd255 100644
--- a/api/src/main/java/com/ning/billing/payment/api/PaymentInfo.java
+++ b/api/src/main/java/com/ning/billing/payment/api/PaymentInfo.java
@@ -71,13 +71,13 @@ public class PaymentInfo implements EventBusNotification {
this.amount = amount;
this.appliedCreditBalanceAmount = appliedCreditBalanceAmount;
this.bankIdentificationNumber = bankIdentificationNumber;
- this.createdDate = createdDate;
this.effectiveDate = effectiveDate;
this.paymentNumber = paymentNumber;
this.referenceId = referenceId;
this.refundAmount = refundAmount;
this.status = status;
this.type = type;
+ this.createdDate = createdDate;
this.updatedDate = updatedDate;
}
diff --git a/payment/src/main/java/com/ning/billing/payment/api/DefaultPaymentApi.java b/payment/src/main/java/com/ning/billing/payment/api/DefaultPaymentApi.java
index 33e963d..943d04f 100644
--- a/payment/src/main/java/com/ning/billing/payment/api/DefaultPaymentApi.java
+++ b/payment/src/main/java/com/ning/billing/payment/api/DefaultPaymentApi.java
@@ -16,6 +16,7 @@
package com.ning.billing.payment.api;
+import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
@@ -27,6 +28,7 @@ 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;
@@ -34,14 +36,17 @@ public class DefaultPaymentApi implements PaymentApi {
private final PaymentProviderPluginRegistry pluginRegistry;
private final AccountUserApi accountUserApi;
private final InvoicePaymentApi invoicePaymentApi;
+ private final PaymentDao paymentDao;
@Inject
public DefaultPaymentApi(PaymentProviderPluginRegistry pluginRegistry,
AccountUserApi accountUserApi,
- InvoicePaymentApi invoicePaymentApi) {
+ InvoicePaymentApi invoicePaymentApi,
+ PaymentDao paymentDao) {
this.pluginRegistry = pluginRegistry;
this.accountUserApi = accountUserApi;
this.invoicePaymentApi = invoicePaymentApi;
+ this.paymentDao = paymentDao;
}
@Override
@@ -55,9 +60,17 @@ public class DefaultPaymentApi implements PaymentApi {
if (accountKey != null) {
final Account account = accountUserApi.getAccountByKey(accountKey);
- if (account != null) {
- paymentProviderName = account.getPaymentProviderName();
- }
+ 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);
@@ -101,14 +114,45 @@ public class DefaultPaymentApi implements PaymentApi {
@Override
public List<Either<PaymentError, PaymentInfo>> createPayment(String accountKey, List<String> invoiceIds) {
- final PaymentProviderPlugin plugin = getPaymentProviderPlugin(accountKey);
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));
- Either<PaymentError, PaymentInfo> paymentOrError = plugin.processInvoice(account, invoice);
- processedPaymentsOrErrors.add(paymentOrError);
+
+ if (invoice.getAmountOutstanding().compareTo(BigDecimal.ZERO) == 0 ) {
+ // TODO: send a notification that invoice was ignored ?
+ }
+ else {
+ PaymentAttempt paymentAttempt = paymentDao.createPaymentAttempt(invoice);
+ Either<PaymentError, PaymentInfo> paymentOrError = plugin.processInvoice(account, invoice);
+ processedPaymentsOrErrors.add(paymentOrError);
+
+ if (paymentOrError.isRight()) {
+ PaymentInfo info = paymentOrError.getRight();
+
+ paymentDao.savePaymentInfo(info);
+ paymentDao.updatePaymentAttemptWithPaymentId(paymentAttempt.getPaymentAttemptId(), info.getId());
+
+ invoicePaymentApi.paymentSuccessful(invoice.getId(),
+ invoice.getAmountOutstanding(),
+ invoice.getCurrency(),
+ paymentAttempt.getPaymentAttemptId(),
+ paymentAttempt.getPaymentAttemptDate());
+ }
+ else {
+ invoicePaymentApi.paymentFailed(invoice.getId(),
+ paymentAttempt.getPaymentAttemptId(),
+ paymentAttempt.getPaymentAttemptDate());
+ }
+ }
}
return processedPaymentsOrErrors;
@@ -116,7 +160,7 @@ public class DefaultPaymentApi implements PaymentApi {
@Override
public Either<PaymentError, PaymentProviderAccount> createPaymentProviderAccount(PaymentProviderAccount account) {
- final PaymentProviderPlugin plugin = getPaymentProviderPlugin(null);
+ final PaymentProviderPlugin plugin = getPaymentProviderPlugin((Account)null);
return plugin.createPaymentProviderAccount(account);
}
@@ -125,4 +169,10 @@ public class DefaultPaymentApi implements PaymentApi {
// TODO Auto-generated method stub
return null;
}
+
+ @Override
+ public PaymentAttempt getPaymentAttemptForPaymentId(String id) {
+ return paymentDao.getPaymentAttemptForPaymentId(id);
+ }
+
}
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..f14dee1
--- /dev/null
+++ b/payment/src/main/java/com/ning/billing/payment/dao/DefaultPaymentDao.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.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 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 id) {
+ sqlDao.updatePaymentAttemptWithPaymentId(paymentAttemptId.toString(), id);
+ }
+
+}
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
index f16adb0..db8e011 100644
--- a/payment/src/main/java/com/ning/billing/payment/dao/PaymentDao.java
+++ b/payment/src/main/java/com/ning/billing/payment/dao/PaymentDao.java
@@ -16,6 +16,20 @@
package com.ning.billing.payment.dao;
-public class PaymentDao {
+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);
}
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..8f38958
--- /dev/null
+++ b/payment/src/main/java/com/ning/billing/payment/dao/PaymentSqlDao.java
@@ -0,0 +1,174 @@
+/*
+ * 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.invoice.api.Invoice;
+import com.ning.billing.invoice.model.DefaultInvoice;
+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("paymentId") String paymentId);
+
+ @SqlUpdate
+ void updatePaymentAttemptWithPaymentId(String paymentAttemptId, String 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) {
+ Invoice invoice = paymentAttempt.getInvoice();
+
+ stmt.bind("id", paymentAttempt.getPaymentAttemptId().toString());
+ stmt.bind("payment_id", paymentAttempt.getPaymentId());
+ stmt.bind("payment_attempt_dt", getDate(paymentAttempt.getPaymentAttemptDate()));
+ stmt.bind("invoice_id", invoice.getId().toString());
+ stmt.bind("account_id", invoice.getAccountId().toString());
+ stmt.bind("invoice_dt", getDate(invoice.getInvoiceDate()));
+ stmt.bind("target_dt", getDate(invoice.getTargetDate()));
+ stmt.bind("amount_paid", invoice.getAmountPaid());
+ stmt.bind("amount_outstanding", invoice.getAmountOutstanding());
+ stmt.bind("last_payment_attempt", getDate(invoice.getLastPaymentAttempt()));
+ stmt.bind("currency", invoice.getCurrency().toString());
+ }
+ }
+
+ 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("id"));
+ String paymentId = rs.getString("payment_id");
+ DateTime paymentAttemptDate = getDate(rs, "payment_attempt_dt");
+
+ UUID invoiceId = UUID.fromString(rs.getString("invoice_id"));
+ UUID accountId = UUID.fromString(rs.getString("account_id"));
+ DateTime invoiceDate = getDate(rs, "invoice_dt");
+ DateTime targetDate = getDate(rs, "target_dt");
+ Currency currency = Currency.valueOf(rs.getString("currency"));
+ DateTime lastPaymentAttempt = getDate(rs, "last_payment_attempt");
+ BigDecimal amountPaid = rs.getBigDecimal("amount_paid");
+
+ Invoice invoice = new DefaultInvoice(invoiceId, accountId, invoiceDate, targetDate, currency, lastPaymentAttempt, amountPaid);
+
+ return new PaymentAttempt(paymentAttemptId, invoice, paymentId, paymentAttemptDate);
+ }
+ }
+
+ 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("id", paymentInfo.getId().toString());
+ stmt.bind("amount", paymentInfo.getAmount());
+ stmt.bind("refund_amount", paymentInfo.getRefundAmount());
+ stmt.bind("applied_credit_balance_amount", paymentInfo.getAppliedCreditBalanceAmount());
+ stmt.bind("payment_number", paymentInfo.getPaymentNumber());
+ stmt.bind("bank_identification_number", paymentInfo.getBankIdentificationNumber());
+ stmt.bind("status", paymentInfo.getStatus());
+ stmt.bind("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 id = rs.getString("id");
+ BigDecimal amount = rs.getBigDecimal("amount");
+ BigDecimal refundAmount = rs.getBigDecimal("refund_amount");
+ BigDecimal appliedCreditBalanceAmount = rs.getBigDecimal("applied_credit_balance_amount");
+ String paymentNumber = rs.getString("payment_number");
+ String bankIdentificationNumber = rs.getString("bank_identification_number");
+ String status = rs.getString("status");
+ String type = rs.getString("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(id,
+ amount,
+ appliedCreditBalanceAmount,
+ bankIdentificationNumber,
+ paymentNumber,
+ refundAmount,
+ status,
+ type,
+ referenceId,
+ effectiveDate,
+ createdDate,
+ updatedDate);
+ }
+ }
+}
diff --git a/payment/src/main/java/com/ning/billing/payment/RequestProcessor.java b/payment/src/main/java/com/ning/billing/payment/RequestProcessor.java
index 5b671bc..39a3d67 100644
--- a/payment/src/main/java/com/ning/billing/payment/RequestProcessor.java
+++ b/payment/src/main/java/com/ning/billing/payment/RequestProcessor.java
@@ -16,18 +16,16 @@
package com.ning.billing.payment;
-import java.math.BigDecimal;
-import java.util.UUID;
+import java.util.Arrays;
+import java.util.List;
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.Invoice;
import com.ning.billing.invoice.api.InvoiceCreationNotification;
-import com.ning.billing.invoice.api.InvoicePaymentApi;
import com.ning.billing.payment.api.Either;
-import com.ning.billing.payment.api.PaymentAttempt;
+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;
@@ -37,18 +35,18 @@ import com.ning.billing.util.eventbus.EventBus.EventBusException;
public class RequestProcessor {
public static final String PAYMENT_PROVIDER_KEY = "paymentProvider";
- private final InvoicePaymentApi invoiceApi;
private final AccountUserApi accountUserApi;
+ private final PaymentApi paymentApi;
private final PaymentProviderPluginRegistry pluginRegistry;
private final EventBus eventBus;
@Inject
public RequestProcessor(AccountUserApi accountUserApi,
- InvoicePaymentApi invoiceApi,
+ PaymentApi paymentApi,
PaymentProviderPluginRegistry pluginRegistry,
EventBus eventBus) {
- this.invoiceApi = invoiceApi;
this.accountUserApi = accountUserApi;
+ this.paymentApi = paymentApi;
this.pluginRegistry = pluginRegistry;
this.eventBus = eventBus;
}
@@ -56,44 +54,20 @@ public class RequestProcessor {
@Subscribe
public void receiveInvoice(InvoiceCreationNotification event) {
try {
- final Invoice invoice = invoiceApi.getInvoice(event.getInvoiceId());
- if (invoice == null) {
+ final Account account = accountUserApi.getAccountById(event.getAccountId());
+
+ if (account == null) {
// TODO: log a warning
}
else {
- PaymentAttempt paymentAttempt = new PaymentAttempt(UUID.randomUUID(), invoice);
+ List<Either<PaymentError, PaymentInfo>> results = paymentApi.createPayment(account, Arrays.asList(event.getInvoiceId().toString()));
- if (invoice.getAmountOutstanding().compareTo(BigDecimal.ZERO) == 0 ) {
- // TODO: send a notification that invoice was ignored ?
+ if (results.isEmpty()) {
+ eventBus.post(new PaymentError("unknown", "No payment processed"));
}
else {
- final Account account = accountUserApi.getAccountById(event.getAccountId());
-
- if (account == null) {
- // TODO: log a warning
- }
- else {
- final BigDecimal paymentAmount = invoice.getAmountOutstanding();
- final String paymentProviderName = account.getPaymentProviderName();
- final PaymentProviderPlugin plugin = pluginRegistry.getPlugin(paymentProviderName);
-
- Either<PaymentError, PaymentInfo> result = plugin.processInvoice(account, invoice);
-
- if (result.isLeft()) {
- invoiceApi.paymentFailed(invoice.getId(),
- paymentAttempt.getPaymentAttemptId(),
- paymentAttempt.getPaymentAttemptDate());
- }
- else {
- updatePaymentAttemptWithPaymentInfoId(result.getRight().getId(), plugin);
- invoiceApi.paymentSuccessful(invoice.getId(),
- paymentAmount,
- invoice.getCurrency(),
- paymentAttempt.getPaymentAttemptId(),
- paymentAttempt.getPaymentAttemptDate());
- }
- eventBus.post(result.isLeft() ? result.getLeft() : result.getRight());
- }
+ Either<PaymentError, PaymentInfo> result = results.get(0);
+ eventBus.post(result.isLeft() ? result.getLeft() : result.getRight());
}
}
}
@@ -102,10 +76,6 @@ public class RequestProcessor {
}
}
- private void updatePaymentAttemptWithPaymentInfoId(String id, PaymentProviderPlugin plugin) {
- // TODO update PaymentAttempt with paymentId
- }
-
@Subscribe
public void receivePaymentInfoRequest(PaymentInfoRequest paymentInfoRequest) throws EventBusException {
final Account account = accountUserApi.getAccountById(paymentInfoRequest.getAccountId());
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
index 9c81c77..8b40409 100644
--- a/payment/src/main/java/com/ning/billing/payment/setup/PaymentModule.java
+++ b/payment/src/main/java/com/ning/billing/payment/setup/PaymentModule.java
@@ -23,6 +23,8 @@ 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.MockPaymentDao;
+import com.ning.billing.payment.dao.PaymentDao;
import com.ning.billing.payment.provider.PaymentProviderPluginRegistry;
public class PaymentModule extends AbstractModule {
@@ -42,11 +44,13 @@ public class PaymentModule extends AbstractModule {
@Override
protected void configure() {
final ConfigurationObjectFactory factory = new ConfigurationObjectFactory(props);
- final PaymentConfig config = factory.build(PaymentConfig.class);
+ final PaymentConfig paymentConfig = factory.build(PaymentConfig.class);
- bind(PaymentConfig.class).toInstance(config);
+ bind(PaymentConfig.class).toInstance(paymentConfig);
bind(PaymentProviderPluginRegistry.class).asEagerSingleton();
- bind(PaymentApi.class).to(DefaultPaymentApi.class);
- installPaymentProviderPlugins(config);
+ bind(PaymentApi.class).to(DefaultPaymentApi.class).asEagerSingleton();
+ bind(MockPaymentDao.class).asEagerSingleton();
+ bind(PaymentDao.class).to(MockPaymentDao.class);
+ installPaymentProviderPlugins(paymentConfig);
}
}
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..553877e
--- /dev/null
+++ b/payment/src/main/resources/com/ning/billing/payment/dao/PaymentSqlDao.sql.stg
@@ -0,0 +1,2 @@
+group PaymentSqlDao;
+
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..ca46ca3
--- /dev/null
+++ b/payment/src/test/java/com/ning/billing/payment/dao/MockPaymentDao.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.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.getId(), paymentInfo);
+ }
+
+ @Override
+ public void updatePaymentAttemptWithPaymentId(UUID paymentAttemptId, String paymentId) {
+ PaymentAttempt existingPaymentAttempt = paymentAttempts.get(paymentAttemptId);
+ if (existingPaymentAttempt != null) {
+ paymentAttempts.put(existingPaymentAttempt.getPaymentAttemptId(),
+ new PaymentAttempt(existingPaymentAttempt.getPaymentAttemptId(),
+ existingPaymentAttempt.getInvoice(),
+ paymentId));
+ }
+ }
+
+}
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
index a02dfed..23ff1d3 100644
--- a/payment/src/test/java/com/ning/billing/payment/provider/MockPaymentProviderPlugin.java
+++ b/payment/src/test/java/com/ning/billing/payment/provider/MockPaymentProviderPlugin.java
@@ -71,7 +71,7 @@ public class MockPaymentProviderPlugin implements PaymentProviderPlugin {
PaymentProviderAccount paymentProviderAccount = accounts.put(account.getAccountName(),
new PaymentProviderAccount.Builder().setAccountName(account.getAccountName())
.setAccountNumber(account.getAccountName())
- .setId(account.getId().toString())
+ .setId(account.getId())
.build());
return Either.right(paymentProviderAccount);
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..39a080c
--- /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 org.apache.commons.collections.MapUtils;
+
+import com.google.common.collect.ImmutableMap;
+import com.ning.billing.payment.provider.MockPaymentProviderPluginModule;
+import com.ning.billing.util.eventbus.EventBus;
+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(EventBus.class).to(MemoryEventBus.class).asEagerSingleton();
+ }
+}
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..8ccfefd
--- /dev/null
+++ b/payment/src/test/java/com/ning/billing/payment/TestPaymentInvoiceIntegration.java
@@ -0,0 +1,194 @@
+/*
+ * 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.UUID;
+import java.util.concurrent.Callable;
+
+import org.apache.commons.io.IOUtils;
+import org.apache.commons.lang.RandomStringUtils;
+import org.joda.time.DateTime;
+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.api.user.AccountBuilder;
+import com.ning.billing.account.dao.AccountDao;
+import com.ning.billing.account.glue.AccountModule;
+import com.ning.billing.catalog.api.Currency;
+import com.ning.billing.dbi.MysqlTestingHelper;
+import com.ning.billing.invoice.api.Invoice;
+import com.ning.billing.invoice.api.InvoiceItem;
+import com.ning.billing.invoice.api.InvoicePaymentApi;
+import com.ning.billing.invoice.dao.InvoiceDao;
+import com.ning.billing.invoice.glue.InvoiceModule;
+import com.ning.billing.invoice.model.DefaultInvoice;
+import com.ning.billing.invoice.model.DefaultInvoiceItem;
+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.EventBus;
+import com.ning.billing.util.eventbus.EventBus.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 EventBus eventBus;
+ @Inject
+ private RequestProcessor invoiceProcessor;
+ @Inject
+ protected AccountDao accountDao;
+ @Inject
+ protected InvoiceDao invoiceDao;
+ @Inject
+ protected InvoicePaymentApi invoicePaymentApi;
+ @Inject
+ protected PaymentApi paymentApi;
+
+ 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 invoiceddl = IOUtils.toString(MysqlTestingHelper.class.getResourceAsStream("/com/ning/billing/invoice/ddl.sql"));
+
+ helper = new MysqlTestingHelper();
+ helper.startMysql();
+ helper.initDb(accountddl + "\n" + invoiceddl);
+ 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() {
+ eventBus.stop();
+ }
+
+ @Test(enabled = false)
+ protected 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.save(account);
+ return account;
+ }
+
+ @Test(enabled = false)
+ protected Invoice createTestInvoice(Account account,
+ DateTime targetDate,
+ Currency currency,
+ InvoiceItem... items) {
+ Invoice invoice = new DefaultInvoice(UUID.randomUUID(), account.getId(), new DateTime(), targetDate, currency, null, new BigDecimal("0"));
+
+ for (InvoiceItem item : items) {
+ invoice.add(new DefaultInvoiceItem(invoice.getId(),
+ item.getSubscriptionId(),
+ item.getStartDate(),
+ item.getEndDate(),
+ item.getDescription(),
+ item.getAmount(),
+ item.getRate(),
+ item.getCurrency()));
+ }
+ invoiceDao.save(invoice);
+ return invoice;
+ }
+
+ @Test
+ public void testInvoiceIntegration() throws Exception {
+ final DateTime now = new DateTime();
+ final Account account = createTestAccount();
+ 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);
+ final Invoice invoice = createTestInvoice(account, now, Currency.USD, item);
+
+ 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).getId());
+ Assert.assertNotNull(paymentAttempt);
+
+ invoicePaymentApi.getInvoicePayment(invoice.getId(), paymentAttempt.getPaymentAttemptId());
+ }
+}