killbill-memoizeit

fix jJanitor concurrency issue that update the payment status

10/25/2017 10:00:15 PM

Details

diff --git a/payment/src/main/java/org/killbill/billing/payment/core/janitor/IncompletePaymentTransactionTask.java b/payment/src/main/java/org/killbill/billing/payment/core/janitor/IncompletePaymentTransactionTask.java
index 88fd895..6887cc5 100644
--- a/payment/src/main/java/org/killbill/billing/payment/core/janitor/IncompletePaymentTransactionTask.java
+++ b/payment/src/main/java/org/killbill/billing/payment/core/janitor/IncompletePaymentTransactionTask.java
@@ -175,7 +175,8 @@ public class IncompletePaymentTransactionTask extends CompletionTaskBase<Payment
         final Boolean result = doJanitorOperationWithAccountLock(new JanitorIterationCallback() {
             @Override
             public Boolean doIteration() {
-                return updatePaymentAndTransactionInternal(payment, null, null, paymentTransaction, paymentTransactionInfoPlugin, internalTenantContext);
+                final PaymentTransactionModelDao refreshedPaymentTransaction = paymentDao.getPaymentTransaction(paymentTransaction.getId(), internalTenantContext);
+                return updatePaymentAndTransactionInternal(payment, null, null, refreshedPaymentTransaction, paymentTransactionInfoPlugin, internalTenantContext);
             }
         }, internalTenantContext);
         return result != null && result;
diff --git a/payment/src/test/java/org/killbill/billing/payment/core/janitor/TestIncompletePaymentTransactionTaskWithDB.java b/payment/src/test/java/org/killbill/billing/payment/core/janitor/TestIncompletePaymentTransactionTaskWithDB.java
index 8dc40d7..1a356d0 100644
--- a/payment/src/test/java/org/killbill/billing/payment/core/janitor/TestIncompletePaymentTransactionTaskWithDB.java
+++ b/payment/src/test/java/org/killbill/billing/payment/core/janitor/TestIncompletePaymentTransactionTaskWithDB.java
@@ -21,14 +21,24 @@ import java.math.BigDecimal;
 import java.util.List;
 import java.util.UUID;
 
+import org.joda.time.DateTime;
 import org.killbill.billing.account.api.Account;
+import org.killbill.billing.callcontext.InternalTenantContext;
 import org.killbill.billing.catalog.api.Currency;
 import org.killbill.billing.payment.PaymentTestSuiteWithEmbeddedDB;
 import org.killbill.billing.payment.api.Payment;
 import org.killbill.billing.payment.api.PaymentApiException;
 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.core.sm.PaymentStateMachineHelper;
+import org.killbill.billing.payment.dao.PaymentModelDao;
+import org.killbill.billing.payment.dao.PaymentTransactionModelDao;
 import org.killbill.billing.payment.plugin.api.PaymentPluginStatus;
+import org.killbill.billing.payment.plugin.api.PaymentTransactionInfoPlugin;
+import org.killbill.billing.payment.provider.DefaultNoOpPaymentInfoPlugin;
 import org.killbill.billing.payment.provider.MockPaymentProviderPlugin;
+import org.killbill.billing.util.callcontext.InternalCallContextFactory;
 import org.killbill.billing.util.globallocker.LockerType;
 import org.killbill.commons.locker.GlobalLock;
 import org.killbill.commons.locker.LockFailedException;
@@ -105,4 +115,69 @@ public class TestIncompletePaymentTransactionTaskWithDB extends PaymentTestSuite
             }
         }
     }
+
+    @Test(groups = "slow", description = "https://github.com/killbill/killbill/issues/809")
+    public void testUpdateWithinLock() throws PaymentApiException {
+        final Payment payment = paymentApi.createAuthorization(account,
+                                                          account.getPaymentMethodId(),
+                                                          null,
+                                                          BigDecimal.TEN,
+                                                          Currency.EUR,
+                                                          UUID.randomUUID().toString(),
+                                                          UUID.randomUUID().toString(),
+                                                          ImmutableList.<PluginProperty>of(new PluginProperty(MockPaymentProviderPlugin.PLUGIN_PROPERTY_PAYMENT_PLUGIN_STATUS_OVERRIDE, PaymentPluginStatus.UNDEFINED.toString(), false)),
+                                                          callContext);
+        final PaymentModelDao paymentModel = paymentDao.getPayment(payment.getId(), internalCallContext);
+        final UUID transactionId = payment.getTransactions().get(0).getId();
+        final PaymentTransactionModelDao transactionModel = paymentDao.getPaymentTransaction(transactionId, internalCallContext);
+
+        Assert.assertEquals(paymentModel.getStateName(), "AUTH_ERRORED");
+        Assert.assertEquals(transactionModel.getTransactionStatus().toString(), "UNKNOWN");
+
+        paymentDao.updatePaymentAndTransactionOnCompletion(
+                account.getId(),
+                null,
+                payment.getId(),
+                TransactionType.AUTHORIZE,
+                "AUTH_SUCCESS",
+                "AUTH_SUCCESS",
+                transactionId,
+                TransactionStatus.SUCCESS,
+                BigDecimal.TEN,
+                Currency.EUR,
+                "200",
+                "Ok",
+                internalCallContext);
+
+        paymentApi.createCapture(account,
+                                 payment.getId(),
+                                 BigDecimal.TEN,
+                                 Currency.EUR,
+                                 UUID.randomUUID().toString(),
+                                 ImmutableList.<PluginProperty>of(new PluginProperty(MockPaymentProviderPlugin.PLUGIN_PROPERTY_PAYMENT_PLUGIN_STATUS_OVERRIDE, PaymentPluginStatus.PROCESSED.toString(), false)),
+                                 callContext);
+
+        final PaymentModelDao paymentAfterCapture = paymentDao.getPayment(payment.getId(), internalCallContext);
+        Assert.assertEquals(paymentAfterCapture.getStateName(), "CAPTURE_SUCCESS");
+
+        PaymentTransactionInfoPlugin paymentTransactionInfoPlugin = new DefaultNoOpPaymentInfoPlugin(
+                payment.getId(),
+                transactionId,
+                TransactionType.AUTHORIZE,
+                BigDecimal.TEN,
+                Currency.EUR,
+                transactionModel.getEffectiveDate(),
+                transactionModel.getCreatedDate(),
+                PaymentPluginStatus.PROCESSED,
+                "200",
+                "OK");
+        incompletePaymentTransactionTask.updatePaymentAndTransactionIfNeededWithAccountLock(
+                paymentModel,
+                transactionModel,
+                paymentTransactionInfoPlugin,
+                internalCallContext);
+
+        final PaymentModelDao paymentAfterJanitor = paymentDao.getPayment(payment.getId(), internalCallContext);
+        Assert.assertEquals(paymentAfterJanitor.getStateName(), "CAPTURE_SUCCESS");
+    }
 }