diff --git a/payment/src/main/java/org/killbill/billing/payment/core/PaymentProcessor.java b/payment/src/main/java/org/killbill/billing/payment/core/PaymentProcessor.java
index e4d9edf..f9b3dde 100644
--- a/payment/src/main/java/org/killbill/billing/payment/core/PaymentProcessor.java
+++ b/payment/src/main/java/org/killbill/billing/payment/core/PaymentProcessor.java
@@ -169,30 +169,33 @@ public class PaymentProcessor extends ProcessorBase {
final Map<UUID, PaymentPluginApi> paymentPluginByPaymentMethodId = new HashMap<UUID, PaymentPluginApi>();
final Collection<UUID> absentPlugins = new HashSet<UUID>();
- return Lists.<PaymentModelDao, Payment>transform(paymentsModelDao,
- new Function<PaymentModelDao, Payment>() {
- @Override
- public Payment apply(final PaymentModelDao paymentModelDao) {
- List<PaymentTransactionInfoPlugin> pluginInfo = null;
-
- if (withPluginInfo) {
- PaymentPluginApi pluginApi = paymentPluginByPaymentMethodId.get(paymentModelDao.getPaymentMethodId());
- if (pluginApi == null && !absentPlugins.contains(paymentModelDao.getPaymentMethodId())) {
- try {
- pluginApi = getPaymentProviderPlugin(paymentModelDao.getPaymentMethodId(), tenantContext);
- paymentPluginByPaymentMethodId.put(paymentModelDao.getPaymentMethodId(), pluginApi);
- } catch (final PaymentApiException e) {
- log.warn("Unable to retrieve pluginApi for payment method " + paymentModelDao.getPaymentMethodId());
- absentPlugins.add(paymentModelDao.getPaymentMethodId());
- }
- }
-
- pluginInfo = getPaymentTransactionInfoPluginsIfNeeded(pluginApi, paymentModelDao, context);
- }
-
- return toPayment(paymentModelDao, transactionsModelDao, pluginInfo, tenantContext);
- }
- });
+ final List<Payment> transformedPayments = Lists.<PaymentModelDao, Payment>transform(paymentsModelDao,
+ new Function<PaymentModelDao, Payment>() {
+ @Override
+ public Payment apply(final PaymentModelDao paymentModelDao) {
+ List<PaymentTransactionInfoPlugin> pluginInfo = null;
+
+ if (withPluginInfo) {
+ PaymentPluginApi pluginApi = paymentPluginByPaymentMethodId.get(paymentModelDao.getPaymentMethodId());
+ if (pluginApi == null && !absentPlugins.contains(paymentModelDao.getPaymentMethodId())) {
+ try {
+ pluginApi = getPaymentProviderPlugin(paymentModelDao.getPaymentMethodId(), tenantContext);
+ paymentPluginByPaymentMethodId.put(paymentModelDao.getPaymentMethodId(), pluginApi);
+ } catch (final PaymentApiException e) {
+ log.warn("Unable to retrieve pluginApi for payment method " + paymentModelDao.getPaymentMethodId());
+ absentPlugins.add(paymentModelDao.getPaymentMethodId());
+ }
+ }
+
+ pluginInfo = getPaymentTransactionInfoPluginsIfNeeded(pluginApi, paymentModelDao, context);
+ }
+
+ return toPayment(paymentModelDao, transactionsModelDao, pluginInfo, tenantContext);
+ }
+ });
+
+ // Copy the transformed list, so the transformation function is applied once (otherwise, the Janitor could be invoked multiple times)
+ return ImmutableList.<Payment>copyOf(transformedPayments);
}
public Payment getPayment(final UUID paymentId, final boolean withPluginInfo, final Iterable<PluginProperty> properties, final TenantContext tenantContext, final InternalTenantContext internalTenantContext) throws PaymentApiException {
diff --git a/payment/src/test/java/org/killbill/billing/payment/core/TestPaymentProcessor.java b/payment/src/test/java/org/killbill/billing/payment/core/TestPaymentProcessor.java
index d60f90b..fe0fe2a 100644
--- a/payment/src/test/java/org/killbill/billing/payment/core/TestPaymentProcessor.java
+++ b/payment/src/test/java/org/killbill/billing/payment/core/TestPaymentProcessor.java
@@ -18,7 +18,6 @@
package org.killbill.billing.payment.core;
import java.math.BigDecimal;
-import java.util.Collection;
import java.util.LinkedList;
import java.util.List;
import java.util.UUID;
@@ -28,9 +27,9 @@ import javax.annotation.Nullable;
import org.killbill.billing.account.api.Account;
import org.killbill.billing.catalog.api.Currency;
-import org.killbill.billing.events.BusInternalEvent;
import org.killbill.billing.events.PaymentErrorInternalEvent;
import org.killbill.billing.events.PaymentInfoInternalEvent;
+import org.killbill.billing.events.PaymentInternalEvent;
import org.killbill.billing.events.PaymentPluginErrorInternalEvent;
import org.killbill.billing.payment.PaymentTestSuiteWithEmbeddedDB;
import org.killbill.billing.payment.api.Payment;
@@ -58,11 +57,14 @@ public class TestPaymentProcessor extends PaymentTestSuiteWithEmbeddedDB {
private static final BigDecimal TEN = new BigDecimal("10");
private static final Currency CURRENCY = Currency.BTC;
+ private MockPaymentProviderPlugin mockPaymentProviderPlugin;
private PaymentBusListener paymentBusListener;
private Account account;
@BeforeMethod(groups = "slow")
public void setUp() throws Exception {
+ mockPaymentProviderPlugin = (MockPaymentProviderPlugin) registry.getServiceForName(MockPaymentProviderPlugin.PLUGIN_NAME);
+
account = testHelper.createTestAccount(UUID.randomUUID().toString(), true);
paymentBusListener = new PaymentBusListener();
@@ -70,9 +72,31 @@ public class TestPaymentProcessor extends PaymentTestSuiteWithEmbeddedDB {
}
@Test(groups = "slow")
- public void testClassicFlow() throws Exception {
+ public void testGetAccountPaymentsWithJanitor() throws Exception {
final String paymentExternalKey = UUID.randomUUID().toString();
+ final Iterable<PluginProperty> pluginPropertiesToDriveTransationToUnknown = ImmutableList.<PluginProperty>of(new PluginProperty(MockPaymentProviderPlugin.PLUGIN_PROPERTY_PAYMENT_PLUGIN_STATUS_OVERRIDE, PaymentPluginStatus.UNDEFINED, false));
+
+ final String authorizationKey = UUID.randomUUID().toString();
+ final Payment authorization = paymentProcessor.createAuthorization(true, null, account, null, null, TEN, CURRENCY, paymentExternalKey, authorizationKey,
+ SHOULD_LOCK_ACCOUNT, pluginPropertiesToDriveTransationToUnknown, callContext, internalCallContext);
+ verifyPayment(authorization, paymentExternalKey, ZERO, ZERO, ZERO, 1);
+ final UUID paymentId = authorization.getId();
+ verifyPaymentTransaction(authorization.getTransactions().get(0), authorizationKey, TransactionType.AUTHORIZE, TEN, paymentId);
+ paymentBusListener.verify(0, 0, 1, account.getId(), paymentId, ZERO, TransactionStatus.UNKNOWN);
+
+ mockPaymentProviderPlugin.overridePaymentPluginStatus(paymentId, authorization.getTransactions().get(0).getId(), PaymentPluginStatus.PROCESSED);
+
+ final List<Payment> payments = paymentProcessor.getAccountPayments(account.getId(), true, callContext, internalCallContext);
+ Assert.assertEquals(payments.size(), 1);
+ verifyPayment(payments.get(0), paymentExternalKey, TEN, ZERO, ZERO, 1);
+ verifyPaymentTransaction(payments.get(0).getTransactions().get(0), authorizationKey, TransactionType.AUTHORIZE, TEN, paymentId);
+ paymentBusListener.verify(1, 0, 1, account.getId(), paymentId, TEN, TransactionStatus.SUCCESS);
+ }
+
+ @Test(groups = "slow")
+ public void testClassicFlow() throws Exception {
+ final String paymentExternalKey = UUID.randomUUID().toString();
final Iterable<PluginProperty> pluginPropertiesToDriveTransationToPending = ImmutableList.<PluginProperty>of(new PluginProperty(MockPaymentProviderPlugin.PLUGIN_PROPERTY_PAYMENT_PLUGIN_STATUS_OVERRIDE, PaymentPluginStatus.PENDING, false));
@@ -209,8 +233,8 @@ public class TestPaymentProcessor extends PaymentTestSuiteWithEmbeddedDB {
private static final class PaymentBusListener {
private final List<PaymentInfoInternalEvent> paymentInfoEvents = new LinkedList<PaymentInfoInternalEvent>();
- private final Collection<BusInternalEvent> paymentErrorEvents = new LinkedList<BusInternalEvent>();
- private final Collection<BusInternalEvent> paymentPluginErrorEvents = new LinkedList<BusInternalEvent>();
+ private final List<PaymentInternalEvent> paymentErrorEvents = new LinkedList<PaymentInternalEvent>();
+ private final List<PaymentInternalEvent> paymentPluginErrorEvents = new LinkedList<PaymentInternalEvent>();
@Subscribe
public void paymentInfo(final PaymentInfoInternalEvent event) {
@@ -228,20 +252,28 @@ public class TestPaymentProcessor extends PaymentTestSuiteWithEmbeddedDB {
}
private void verify(final int eventNb, final UUID accountId, final UUID paymentId, final BigDecimal amount, final TransactionStatus transactionStatus) throws Exception {
+ verify(eventNb, 0, 0, accountId, paymentId, amount, transactionStatus);
+ }
+
+ private void verify(final int nbInfoEvents, final int nbErrorEvents, final int nbPluginErrorEvents, final UUID accountId, final UUID paymentId, final BigDecimal amount, final TransactionStatus transactionStatus) throws Exception {
Awaitility.await()
.until(new Callable<Boolean>() {
@Override
public Boolean call() throws Exception {
- return paymentInfoEvents.size() == eventNb;
+ return paymentInfoEvents.size() == nbInfoEvents && paymentErrorEvents.size() == nbErrorEvents && paymentPluginErrorEvents.size() == nbPluginErrorEvents;
}
});
- Assert.assertEquals(paymentErrorEvents.size(), 0);
- Assert.assertEquals(paymentPluginErrorEvents.size(), 0);
- verify(paymentInfoEvents.get(eventNb - 1), accountId, paymentId, amount, transactionStatus);
+ if (transactionStatus == TransactionStatus.SUCCESS || transactionStatus == TransactionStatus.PENDING) {
+ verify(paymentInfoEvents.get(paymentInfoEvents.size() - 1), accountId, paymentId, amount, transactionStatus);
+ } else if (transactionStatus == TransactionStatus.PAYMENT_FAILURE) {
+ verify(paymentErrorEvents.get(paymentErrorEvents.size() - 1), accountId, paymentId, amount, transactionStatus);
+ } else {
+ verify(paymentPluginErrorEvents.get(paymentPluginErrorEvents.size() - 1), accountId, paymentId, amount, transactionStatus);
+ }
}
- private void verify(final PaymentInfoInternalEvent event, final UUID accountId, final UUID paymentId, @Nullable final BigDecimal amount, final TransactionStatus transactionStatus) {
+ private void verify(final PaymentInternalEvent event, final UUID accountId, final UUID paymentId, @Nullable final BigDecimal amount, final TransactionStatus transactionStatus) {
Assert.assertEquals(event.getPaymentId(), paymentId);
Assert.assertEquals(event.getAccountId(), accountId);
if (amount == null) {
diff --git a/payment/src/test/java/org/killbill/billing/payment/provider/MockPaymentProviderPlugin.java b/payment/src/test/java/org/killbill/billing/payment/provider/MockPaymentProviderPlugin.java
index d6fb5f3..8adabea 100644
--- a/payment/src/test/java/org/killbill/billing/payment/provider/MockPaymentProviderPlugin.java
+++ b/payment/src/test/java/org/killbill/billing/payment/provider/MockPaymentProviderPlugin.java
@@ -21,6 +21,7 @@ package org.killbill.billing.payment.provider;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Iterator;
+import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.UUID;
@@ -49,7 +50,6 @@ import org.killbill.billing.util.entity.DefaultPagination;
import org.killbill.billing.util.entity.Pagination;
import org.killbill.clock.Clock;
-import com.google.common.base.MoreObjects;
import com.google.common.base.Predicate;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
@@ -241,6 +241,30 @@ public class MockPaymentProviderPlugin implements PaymentPluginApi {
}
}
+ public void overridePaymentPluginStatus(final UUID kbPaymentId, final UUID kbTransactionId, final PaymentPluginStatus status) {
+ final List<PaymentTransactionInfoPlugin> existingTransactions = paymentTransactions.remove(kbPaymentId.toString());
+ final List<PaymentTransactionInfoPlugin> newTransactions = new LinkedList<PaymentTransactionInfoPlugin>();
+ paymentTransactions.put(kbPaymentId.toString(), newTransactions);
+
+ for (final PaymentTransactionInfoPlugin existingTransaction : existingTransactions) {
+ if (existingTransaction.getKbTransactionPaymentId().equals(kbTransactionId)) {
+ final PaymentTransactionInfoPlugin newTransaction = new DefaultNoOpPaymentInfoPlugin(existingTransaction.getKbPaymentId(),
+ existingTransaction.getKbTransactionPaymentId(),
+ existingTransaction.getTransactionType(),
+ existingTransaction.getAmount(),
+ existingTransaction.getCurrency(),
+ existingTransaction.getEffectiveDate(),
+ existingTransaction.getCreatedDate(),
+ status,
+ existingTransaction.getGatewayErrorCode(),
+ existingTransaction.getGatewayError());
+ newTransactions.add(newTransaction);
+ } else {
+ newTransactions.add(existingTransaction);
+ }
+ }
+ }
+
@Override
public PaymentTransactionInfoPlugin authorizePayment(final UUID kbAccountId, final UUID kbPaymentId, final UUID kbTransactionId, final UUID kbPaymentMethodId, final BigDecimal amount, final Currency currency, final Iterable<PluginProperty> properties, final CallContext context)
throws PaymentPluginApiException {