killbill-uncached
Changes
payment/src/main/java/org/killbill/billing/payment/core/janitor/IncompletePaymentAttemptTask.java 10(+5 -5)
payment/src/main/java/org/killbill/billing/payment/core/janitor/PendingPaymentTransactionTask.java 10(+5 -5)
payment/src/main/java/org/killbill/billing/payment/core/janitor/UnknownPaymentTransactionTask.java 12(+6 -6)
Details
diff --git a/payment/src/main/java/org/killbill/billing/payment/core/janitor/Janitor.java b/payment/src/main/java/org/killbill/billing/payment/core/janitor/Janitor.java
index 12d71a0..b202a5d 100644
--- a/payment/src/main/java/org/killbill/billing/payment/core/janitor/Janitor.java
+++ b/payment/src/main/java/org/killbill/billing/payment/core/janitor/Janitor.java
@@ -48,9 +48,9 @@ public class Janitor {
private final ScheduledExecutorService janitorExecutor;
private final PaymentConfig paymentConfig;
- private final PendingTransactionTask pendingTransactionTask;
- private final AttemptCompletionTask attemptCompletionTask;
- private final ErroredPaymentTask erroredPaymentCompletionTask;
+ private final PendingPaymentTransactionTask pendingPaymentTransactionTask;
+ private final IncompletePaymentAttemptTask incompletePaymentAttemptTask;
+ private final UnknownPaymentTransactionTask erroredPaymentCompletionTask;
private volatile boolean isStopped;
@@ -67,11 +67,11 @@ public class Janitor {
final OSGIServiceRegistration<PaymentPluginApi> pluginRegistry) {
this.janitorExecutor = janitorExecutor;
this.paymentConfig = paymentConfig;
- this.pendingTransactionTask = new PendingTransactionTask(this, internalCallContextFactory, paymentConfig, paymentDao, clock, paymentSMHelper, retrySMHelper,
+ this.pendingPaymentTransactionTask = new PendingPaymentTransactionTask(this, internalCallContextFactory, paymentConfig, paymentDao, clock, paymentSMHelper, retrySMHelper,
accountInternalApi, pluginControlledPaymentAutomatonRunner, pluginRegistry);
- this.attemptCompletionTask = new AttemptCompletionTask(this, internalCallContextFactory, paymentConfig, paymentDao, clock, paymentSMHelper, retrySMHelper,
+ this.incompletePaymentAttemptTask = new IncompletePaymentAttemptTask(this, internalCallContextFactory, paymentConfig, paymentDao, clock, paymentSMHelper, retrySMHelper,
accountInternalApi, pluginControlledPaymentAutomatonRunner, pluginRegistry);
- this.erroredPaymentCompletionTask = new ErroredPaymentTask(this, internalCallContextFactory, paymentConfig, paymentDao, clock, paymentSMHelper, retrySMHelper,
+ this.erroredPaymentCompletionTask = new UnknownPaymentTransactionTask(this, internalCallContextFactory, paymentConfig, paymentDao, clock, paymentSMHelper, retrySMHelper,
accountInternalApi, pluginControlledPaymentAutomatonRunner, pluginRegistry);
this.isStopped = false;
}
@@ -85,12 +85,12 @@ public class Janitor {
// Start task for removing old pending payments.
final TimeUnit pendingRateUnit = paymentConfig.getJanitorRunningRate().getUnit();
final long pendingPeriod = paymentConfig.getJanitorRunningRate().getPeriod();
- janitorExecutor.scheduleAtFixedRate(pendingTransactionTask, pendingPeriod, pendingPeriod, pendingRateUnit);
+ janitorExecutor.scheduleAtFixedRate(pendingPaymentTransactionTask, pendingPeriod, pendingPeriod, pendingRateUnit);
// Start task for completing incomplete payment attempts
final TimeUnit attemptCompletionRateUnit = paymentConfig.getJanitorRunningRate().getUnit();
final long attemptCompletionPeriod = paymentConfig.getJanitorRunningRate().getPeriod();
- janitorExecutor.scheduleAtFixedRate(attemptCompletionTask, attemptCompletionPeriod, attemptCompletionPeriod, attemptCompletionRateUnit);
+ janitorExecutor.scheduleAtFixedRate(incompletePaymentAttemptTask, attemptCompletionPeriod, attemptCompletionPeriod, attemptCompletionRateUnit);
// Start task for completing incomplete payment attempts
final TimeUnit erroredCompletionRateUnit = paymentConfig.getJanitorRunningRate().getUnit();
diff --git a/payment/src/test/java/org/killbill/billing/payment/api/TestPaymentApi.java b/payment/src/test/java/org/killbill/billing/payment/api/TestPaymentApi.java
index b795594..21fde98 100644
--- a/payment/src/test/java/org/killbill/billing/payment/api/TestPaymentApi.java
+++ b/payment/src/test/java/org/killbill/billing/payment/api/TestPaymentApi.java
@@ -535,7 +535,7 @@ public class TestPaymentApi extends PaymentTestSuiteWithEmbeddedDB {
} catch (final PaymentApiException e) {
Assert.assertEquals(e.getCode(), ErrorCode.PAYMENT_NO_SUCH_PAYMENT.getCode());
- final Payment latestPayment = paymentApi.getPayment(initialPayment.getId(), false, ImmutableList.<PluginProperty>of(), callContext);
+ final Payment latestPayment = paymentApi.getPayment(initialPayment.getId(), true, ImmutableList.<PluginProperty>of(), callContext);
assertEquals(latestPayment, initialPayment);
}
}
@@ -555,7 +555,7 @@ public class TestPaymentApi extends PaymentTestSuiteWithEmbeddedDB {
} catch (final PaymentApiException e) {
Assert.assertEquals(e.getCode(), ErrorCode.PAYMENT_INVALID_PARAMETER.getCode());
- final Payment latestPayment = paymentApi.getPayment(initialPayment.getId(), false, ImmutableList.<PluginProperty>of(), callContext);
+ final Payment latestPayment = paymentApi.getPayment(initialPayment.getId(), true, ImmutableList.<PluginProperty>of(), callContext);
assertEquals(latestPayment, initialPayment);
}
}
diff --git a/payment/src/test/java/org/killbill/billing/payment/PaymentTestSuiteWithEmbeddedDB.java b/payment/src/test/java/org/killbill/billing/payment/PaymentTestSuiteWithEmbeddedDB.java
index 3d144dc..81c4813 100644
--- a/payment/src/test/java/org/killbill/billing/payment/PaymentTestSuiteWithEmbeddedDB.java
+++ b/payment/src/test/java/org/killbill/billing/payment/PaymentTestSuiteWithEmbeddedDB.java
@@ -86,6 +86,7 @@ public abstract class PaymentTestSuiteWithEmbeddedDB extends GuicyKillbillTestSu
super.beforeMethod();
eventBus.start();
Profiling.resetPerThreadProfilingData();
+ clock.resetDeltaFromReality();
}
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 28f90ed..f161297 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
@@ -19,6 +19,7 @@
package org.killbill.billing.payment.provider;
import java.math.BigDecimal;
+import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.UUID;
@@ -61,6 +62,8 @@ public class MockPaymentProviderPlugin implements NoOpPaymentPluginApi {
private final AtomicBoolean makeAllInvoicesFailWithError = new AtomicBoolean(false);
private final Map<String, InternalPaymentInfo> payments = new ConcurrentHashMap<String, InternalPaymentInfo>();
+ private final Map<String, List<PaymentTransactionInfoPlugin>> paymentTransactions = new ConcurrentHashMap<String, List<PaymentTransactionInfoPlugin>>();
+
// Note: we can't use HashMultiMap as we care about storing duplicate key/value pairs
private final Map<String, PaymentMethodPlugin> paymentMethods = new ConcurrentHashMap<String, PaymentMethodPlugin>();
private final Map<String, PaymentMethodInfoPlugin> paymentMethodsInfo = new ConcurrentHashMap<String, PaymentMethodInfoPlugin>();
@@ -183,6 +186,7 @@ public class MockPaymentProviderPlugin implements NoOpPaymentPluginApi {
makeNextInvoiceFailWithError.set(false);
paymentMethods.clear();
payments.clear();
+ paymentTransactions.clear();
paymentMethodsInfo.clear();
}
@@ -232,14 +236,8 @@ public class MockPaymentProviderPlugin implements NoOpPaymentPluginApi {
@Override
public List<PaymentTransactionInfoPlugin> getPaymentInfo(final UUID kbAccountId, final UUID kbPaymentId, final Iterable<PluginProperty> properties, final TenantContext context) throws PaymentPluginApiException {
- /*
- final InternalPaymentInfo paymentInfo = payments.get(kbPaymentId.toString());
- if (paymentInfo == null) {
- throw new PaymentPluginApiException("", "No payment found for payment id " + kbPaymentId.toString());
- }
- */
- // Can't be implemented because we did not keep transaction details.
- return ImmutableList.<PaymentTransactionInfoPlugin>of();
+ final List<PaymentTransactionInfoPlugin> result = paymentTransactions.get(kbPaymentId.toString());
+ return result != null ? result : ImmutableList.<PaymentTransactionInfoPlugin>of();
}
@Override
@@ -346,6 +344,13 @@ public class MockPaymentProviderPlugin implements NoOpPaymentPluginApi {
info.addAmount(type, amount);
final PaymentTransactionInfoPlugin result = new DefaultNoOpPaymentInfoPlugin(kbPaymentId, kbTransactionId, type, amount, currency, clock.getUTCNow(), clock.getUTCNow(), status, null);
+ List<PaymentTransactionInfoPlugin> existingTransactions = paymentTransactions.get(kbPaymentId.toString());
+ if (existingTransactions == null) {
+ existingTransactions = new ArrayList<PaymentTransactionInfoPlugin>();
+ paymentTransactions.put(kbPaymentId.toString(), existingTransactions);
+ }
+
+ existingTransactions.add(result);
return result;
}
}
diff --git a/payment/src/test/java/org/killbill/billing/payment/TestJanitor.java b/payment/src/test/java/org/killbill/billing/payment/TestJanitor.java
index 425fada..57e2235 100644
--- a/payment/src/test/java/org/killbill/billing/payment/TestJanitor.java
+++ b/payment/src/test/java/org/killbill/billing/payment/TestJanitor.java
@@ -30,18 +30,19 @@ import org.killbill.billing.invoice.api.Invoice;
import org.killbill.billing.invoice.api.InvoiceApiException;
import org.killbill.billing.invoice.api.InvoiceItem;
import org.killbill.billing.payment.api.Payment;
-import org.killbill.billing.payment.api.PaymentTransaction;
import org.killbill.billing.payment.api.PaymentApiException;
import org.killbill.billing.payment.api.PaymentOptions;
+import org.killbill.billing.payment.api.PaymentTransaction;
import org.killbill.billing.payment.api.PluginProperty;
import org.killbill.billing.payment.api.TransactionStatus;
import org.killbill.billing.payment.api.TransactionType;
-import org.killbill.billing.payment.invoice.InvoicePaymentRoutingPluginApi;
import org.killbill.billing.payment.core.janitor.Janitor;
import org.killbill.billing.payment.dao.PaymentAttemptModelDao;
+import org.killbill.billing.payment.invoice.InvoicePaymentRoutingPluginApi;
import org.killbill.billing.payment.provider.MockPaymentProviderPlugin;
import org.killbill.billing.platform.api.KillbillConfigSource;
import org.killbill.bus.api.PersistentBus.EventBusException;
+import org.testng.Assert;
import org.testng.annotations.AfterClass;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeClass;
@@ -68,7 +69,6 @@ public class TestJanitor extends PaymentTestSuiteWithEmbeddedDB {
}
};
-
@Inject
private Janitor janitor;
@@ -94,7 +94,6 @@ public class TestJanitor extends PaymentTestSuiteWithEmbeddedDB {
janitor.stop();
}
-
@BeforeMethod(groups = "slow")
public void beforeMethod() throws Exception {
super.beforeMethod();
@@ -131,7 +130,7 @@ public class TestJanitor extends PaymentTestSuiteWithEmbeddedDB {
Currency.USD));
final Payment payment = paymentApi.createPurchaseWithPaymentControl(account, account.getPaymentMethodId(), null, requestedAmount, Currency.USD, paymentExternalKey, transactionExternalKey,
- createPropertiesForInvoice(invoice), INVOICE_PAYMENT, callContext);
+ createPropertiesForInvoice(invoice), INVOICE_PAYMENT, callContext);
assertEquals(payment.getTransactions().size(), 1);
assertEquals(payment.getTransactions().get(0).getTransactionStatus(), TransactionStatus.SUCCESS);
assertEquals(payment.getTransactions().get(0).getTransactionType(), TransactionType.PURCHASE);
@@ -148,7 +147,11 @@ public class TestJanitor extends PaymentTestSuiteWithEmbeddedDB {
assertEquals(attempt2.getStateName(), "INIT");
clock.addDays(1);
- try { Thread.sleep(1500); } catch (InterruptedException e) {};
+ try {
+ Thread.sleep(1500);
+ } catch (InterruptedException e) {
+ }
+ ;
final PaymentAttemptModelDao attempt3 = paymentDao.getPaymentAttempt(attempt.getId(), internalCallContext);
assertEquals(attempt3.getStateName(), "SUCCESS");
@@ -180,7 +183,7 @@ public class TestJanitor extends PaymentTestSuiteWithEmbeddedDB {
invoice.addInvoiceItem(invoiceItem);
final Payment payment = paymentApi.createPurchaseWithPaymentControl(account, account.getPaymentMethodId(), null, requestedAmount, Currency.USD, paymentExternalKey, transactionExternalKey,
- createPropertiesForInvoice(invoice), INVOICE_PAYMENT, callContext);
+ createPropertiesForInvoice(invoice), INVOICE_PAYMENT, callContext);
final List<PluginProperty> refundProperties = new ArrayList<PluginProperty>();
final HashMap<UUID, BigDecimal> uuidBigDecimalHashMap = new HashMap<UUID, BigDecimal>();
@@ -189,7 +192,7 @@ public class TestJanitor extends PaymentTestSuiteWithEmbeddedDB {
refundProperties.add(refundIdsProp);
final Payment payment2 = paymentApi.createRefundWithPaymentControl(account, payment.getId(), null, Currency.USD, transactionExternalKey2,
- refundProperties, INVOICE_PAYMENT, callContext);
+ refundProperties, INVOICE_PAYMENT, callContext);
assertEquals(payment2.getTransactions().size(), 2);
PaymentTransaction refundTransaction = payment2.getTransactions().get(1);
@@ -207,15 +210,71 @@ public class TestJanitor extends PaymentTestSuiteWithEmbeddedDB {
assertEquals(attempt2.getStateName(), "INIT");
clock.addDays(1);
- try { Thread.sleep(1500); } catch (InterruptedException e) {};
+ try {
+ Thread.sleep(1500);
+ } catch (InterruptedException e) {
+ }
+ ;
final PaymentAttemptModelDao attempt3 = paymentDao.getPaymentAttempt(refundAttempt.getId(), internalCallContext);
assertEquals(attempt3.getStateName(), "SUCCESS");
+ }
+
+ @Test(groups = "slow")
+ public void testUnknownEntries() throws PaymentApiException, InvoiceApiException, EventBusException {
+
+ final BigDecimal requestedAmount = BigDecimal.TEN;
+ final String paymentExternalKey = "qwru";
+ final String transactionExternalKey = "lkjdsf";
+
+ final Payment payment = paymentApi.createAuthorization(account, account.getPaymentMethodId(), null, requestedAmount, account.getCurrency(), paymentExternalKey,
+ transactionExternalKey, ImmutableList.<PluginProperty>of(), callContext);
+
+ // Artificially move the transaction status to UNKNOWN
+ final String paymentStateName = paymentSMHelper.getErroredStateForTransaction(TransactionType.AUTHORIZE).toString();
+ paymentDao.updatePaymentAndTransactionOnCompletion(account.getId(), payment.getId(), TransactionType.AUTHORIZE, paymentStateName, paymentStateName,
+ payment.getTransactions().get(0).getId(), TransactionStatus.UNKNOWN, requestedAmount, account.getCurrency(),
+ "foo", "bar", internalCallContext);
+ // The UnknownPaymentTransactionTask will look for UNKNOWN payment that *just happened* (in the last SAFETY_DELAY_MS=3min delay), and that are not too old (less than 3 days)
+ clock.addDays(1);
+ try {
+ Thread.sleep(1500);
+ } catch (InterruptedException e) {
+ }
+
+ final Payment updatedPayment = paymentApi.getPayment(payment.getId(), false, ImmutableList.<PluginProperty>of(), callContext);
+ assertEquals(updatedPayment.getTransactions().get(0).getTransactionStatus(), TransactionStatus.SUCCESS);
}
+ @Test(groups = "slow")
+ public void testPendingEntries() throws PaymentApiException, InvoiceApiException, EventBusException {
+
+ final BigDecimal requestedAmount = BigDecimal.TEN;
+ final String paymentExternalKey = "jhj44";
+ final String transactionExternalKey = "4jhjj2";
+
+ final Payment payment = paymentApi.createAuthorization(account, account.getPaymentMethodId(), null, requestedAmount, account.getCurrency(), paymentExternalKey,
+ transactionExternalKey, ImmutableList.<PluginProperty>of(), callContext);
+
+ // Artificially move the transaction status to PENDING
+ final String paymentStateName = paymentSMHelper.getPendingStateForTransaction(TransactionType.AUTHORIZE).toString();
+ paymentDao.updatePaymentAndTransactionOnCompletion(account.getId(), payment.getId(), TransactionType.AUTHORIZE, paymentStateName, paymentStateName,
+ payment.getTransactions().get(0).getId(), TransactionStatus.PENDING, requestedAmount, account.getCurrency(),
+ "loup", "chat", internalCallContext);
+ clock.addDays(1);
+ try {
+ Thread.sleep(1500);
+ } catch (InterruptedException e) {
+ }
+
+ final Payment updatedPayment = paymentApi.getPayment(payment.getId(), false, ImmutableList.<PluginProperty>of(), callContext);
+ Assert.assertEquals(updatedPayment.getTransactions().get(0).getTransactionStatus(), TransactionStatus.PAYMENT_FAILURE);
+
+ }
+
private List<PluginProperty> createPropertiesForInvoice(final Invoice invoice) {
final List<PluginProperty> result = new ArrayList<PluginProperty>();
result.add(new PluginProperty(InvoicePaymentRoutingPluginApi.PROP_IPCD_INVOICE_ID, invoice.getId().toString(), false));