killbill-memoizeit

payment: add tests for completion operations This is part

8/11/2015 7:27:28 PM

Details

diff --git a/payment/src/main/java/org/killbill/billing/payment/core/sm/PaymentStateContext.java b/payment/src/main/java/org/killbill/billing/payment/core/sm/PaymentStateContext.java
index d9c097c..cbe72e7 100644
--- a/payment/src/main/java/org/killbill/billing/payment/core/sm/PaymentStateContext.java
+++ b/payment/src/main/java/org/killbill/billing/payment/core/sm/PaymentStateContext.java
@@ -171,11 +171,13 @@ public class PaymentStateContext {
     }
 
     public BigDecimal getAmount() {
-        return amount;
+        // For a complete operation, if the amount isn't specified, take the original amount on the transaction
+        return amount == null && paymentTransactionModelDao != null ? paymentTransactionModelDao.getAmount() : amount;
     }
 
     public Currency getCurrency() {
-        return currency;
+        // For a complete operation, if the currency isn't specified, take the original currency on the transaction
+        return currency == null && paymentTransactionModelDao != null ? paymentTransactionModelDao.getCurrency() : currency;
     }
 
     public TransactionType getTransactionType() {
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 e4d7b09..91d1d65 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
@@ -24,10 +24,13 @@ import java.util.HashMap;
 import java.util.List;
 import java.util.UUID;
 
+import javax.annotation.Nullable;
+
 import org.joda.time.LocalDate;
 import org.killbill.billing.ErrorCode;
 import org.killbill.billing.account.api.Account;
 import org.killbill.billing.catalog.api.Currency;
+import org.killbill.billing.control.plugin.api.PaymentControlApiException;
 import org.killbill.billing.invoice.api.Invoice;
 import org.killbill.billing.invoice.api.InvoiceApiException;
 import org.killbill.billing.invoice.api.InvoiceItem;
@@ -36,7 +39,8 @@ import org.killbill.billing.payment.PaymentTestSuiteWithEmbeddedDB;
 import org.killbill.billing.payment.dao.PaymentAttemptModelDao;
 import org.killbill.billing.payment.dao.PaymentSqlDao;
 import org.killbill.billing.payment.invoice.InvoicePaymentControlPluginApi;
-import org.killbill.billing.control.plugin.api.PaymentControlApiException;
+import org.killbill.billing.payment.plugin.api.PaymentPluginStatus;
+import org.killbill.billing.payment.provider.MockPaymentProviderPlugin;
 import org.killbill.bus.api.PersistentBus.EventBusException;
 import org.testng.Assert;
 import org.testng.annotations.BeforeMethod;
@@ -608,30 +612,166 @@ public class TestPaymentApi extends PaymentTestSuiteWithEmbeddedDB {
     // Example of a 3D secure payment for instance
     @Test(groups = "slow")
     public void testApiWithPendingPaymentTransaction() throws Exception {
-        final BigDecimal requestedAmount = BigDecimal.TEN;
+        for (final TransactionType transactionType : ImmutableList.<TransactionType>of(TransactionType.AUTHORIZE, TransactionType.PURCHASE, TransactionType.CREDIT)) {
+            testApiWithPendingPaymentTransaction(transactionType, BigDecimal.TEN, BigDecimal.TEN);
+            testApiWithPendingPaymentTransaction(transactionType, BigDecimal.TEN, BigDecimal.ONE);
+            testApiWithPendingPaymentTransaction(transactionType, BigDecimal.TEN, null);
+        }
+    }
 
+    @Test(groups = "slow")
+    public void testApiWithPendingRefundPaymentTransaction() throws Exception {
         final String paymentExternalKey = UUID.randomUUID().toString();
         final String paymentTransactionExternalKey = UUID.randomUUID().toString();
+        final String refundTransactionExternalKey = UUID.randomUUID().toString();
+        final BigDecimal requestedAmount = BigDecimal.TEN;
+        final BigDecimal refundAmount = BigDecimal.ONE;
+        final Iterable<PluginProperty> pendingPluginProperties = ImmutableList.<PluginProperty>of(new PluginProperty(MockPaymentProviderPlugin.PLUGIN_PROPERTY_PAYMENT_PLUGIN_STATUS_OVERRIDE, TransactionStatus.PENDING.toString(), false));
 
-        final Payment pendingPayment = paymentApi.createAuthorization(account, account.getPaymentMethodId(), null, requestedAmount, account.getCurrency(),
-                                                                      paymentExternalKey, paymentTransactionExternalKey, ImmutableList.<PluginProperty>of(), callContext);
+        final Payment payment = createPayment(TransactionType.PURCHASE, null, paymentExternalKey, paymentTransactionExternalKey, requestedAmount, PaymentPluginStatus.PROCESSED);
+        Assert.assertNotNull(payment);
+        Assert.assertEquals(payment.getExternalKey(), paymentExternalKey);
+        Assert.assertEquals(payment.getTransactions().size(), 1);
+        Assert.assertEquals(payment.getTransactions().get(0).getAmount().compareTo(requestedAmount), 0);
+        Assert.assertEquals(payment.getTransactions().get(0).getProcessedAmount().compareTo(requestedAmount), 0);
+        Assert.assertEquals(payment.getTransactions().get(0).getCurrency(), account.getCurrency());
+        Assert.assertEquals(payment.getTransactions().get(0).getExternalKey(), paymentTransactionExternalKey);
+        Assert.assertEquals(payment.getTransactions().get(0).getTransactionStatus(), TransactionStatus.SUCCESS);
 
-        final String paymentStateName = paymentSMHelper.getPendingStateForTransaction(TransactionType.AUTHORIZE).toString();
-        paymentDao.updatePaymentAndTransactionOnCompletion(account.getId(), pendingPayment.getId(), TransactionType.AUTHORIZE, paymentStateName, paymentStateName,
-                                                           pendingPayment.getTransactions().get(0).getId(), TransactionStatus.PENDING, requestedAmount, account.getCurrency(),
-                                                           null, null, internalCallContext);
+        final Payment pendingRefund = paymentApi.createRefund(account,
+                                                              payment.getId(),
+                                                              requestedAmount,
+                                                              account.getCurrency(),
+                                                              refundTransactionExternalKey,
+                                                              pendingPluginProperties,
+                                                              callContext);
+        verifyRefund(pendingRefund, paymentExternalKey, paymentTransactionExternalKey, refundTransactionExternalKey, requestedAmount, requestedAmount, TransactionStatus.PENDING);
+
+        final Payment pendingRefund2 = paymentApi.createRefund(account,
+                                                               payment.getId(),
+                                                               null,
+                                                               null,
+                                                               refundTransactionExternalKey,
+                                                               pendingPluginProperties,
+                                                               callContext);
+        verifyRefund(pendingRefund2, paymentExternalKey, paymentTransactionExternalKey, refundTransactionExternalKey, requestedAmount, requestedAmount, TransactionStatus.PENDING);
+
+        // Note: we change the refund amount
+        final Payment pendingRefund3 = paymentApi.createRefund(account,
+                                                               payment.getId(),
+                                                               refundAmount,
+                                                               account.getCurrency(),
+                                                               refundTransactionExternalKey,
+                                                               pendingPluginProperties,
+                                                               callContext);
+        verifyRefund(pendingRefund3, paymentExternalKey, paymentTransactionExternalKey, refundTransactionExternalKey, requestedAmount, refundAmount, TransactionStatus.PENDING);
+
+        // Pass null, we revert back to the original refund amount
+        final Payment pendingRefund4 = paymentApi.createRefund(account,
+                                                               payment.getId(),
+                                                               null,
+                                                               null,
+                                                               refundTransactionExternalKey,
+                                                               ImmutableList.<PluginProperty>of(),
+                                                               callContext);
+        verifyRefund(pendingRefund4, paymentExternalKey, paymentTransactionExternalKey, refundTransactionExternalKey, requestedAmount, requestedAmount, TransactionStatus.SUCCESS);
 
-        final Payment payment = paymentApi.createAuthorization(account, account.getPaymentMethodId(), pendingPayment.getId(), requestedAmount, account.getCurrency(),
-                                                               paymentExternalKey, paymentTransactionExternalKey, ImmutableList.<PluginProperty>of(), callContext);
+    }
 
-        Assert.assertEquals(payment.getId(), pendingPayment.getId());
-        Assert.assertEquals(payment.getExternalKey(), paymentExternalKey);
-        Assert.assertEquals(payment.getExternalKey(), paymentExternalKey);
+    private void verifyRefund(final Payment refund, final String paymentExternalKey, final String paymentTransactionExternalKey, final String refundTransactionExternalKey, final BigDecimal requestedAmount, final BigDecimal refundAmount, final TransactionStatus transactionStatus) {
+        Assert.assertEquals(refund.getExternalKey(), paymentExternalKey);
+        Assert.assertEquals(refund.getTransactions().size(), 2);
+        Assert.assertEquals(refund.getTransactions().get(0).getAmount().compareTo(requestedAmount), 0);
+        Assert.assertEquals(refund.getTransactions().get(0).getProcessedAmount().compareTo(requestedAmount), 0);
+        Assert.assertEquals(refund.getTransactions().get(0).getCurrency(), account.getCurrency());
+        Assert.assertEquals(refund.getTransactions().get(0).getExternalKey(), paymentTransactionExternalKey);
+        Assert.assertEquals(refund.getTransactions().get(0).getTransactionStatus(), TransactionStatus.SUCCESS);
+        Assert.assertEquals(refund.getTransactions().get(1).getAmount().compareTo(requestedAmount), 0);
+        Assert.assertEquals(refund.getTransactions().get(1).getProcessedAmount().compareTo(refundAmount), 0);
+        Assert.assertEquals(refund.getTransactions().get(1).getCurrency(), account.getCurrency());
+        Assert.assertEquals(refund.getTransactions().get(1).getExternalKey(), refundTransactionExternalKey);
+        Assert.assertEquals(refund.getTransactions().get(1).getTransactionStatus(), transactionStatus);
+    }
 
-        Assert.assertEquals(payment.getTransactions().size(), 1);
-        Assert.assertEquals(payment.getTransactions().get(0).getTransactionStatus(), TransactionStatus.SUCCESS);
-        Assert.assertEquals(payment.getTransactions().get(0).getExternalKey(), paymentTransactionExternalKey);
+    private Payment testApiWithPendingPaymentTransaction(final TransactionType transactionType, final BigDecimal requestedAmount, @Nullable final BigDecimal pendingAmount) throws PaymentApiException {
+        final String paymentExternalKey = UUID.randomUUID().toString();
+        final String paymentTransactionExternalKey = UUID.randomUUID().toString();
 
+        final Payment pendingPayment = createPayment(transactionType, null, paymentExternalKey, paymentTransactionExternalKey, requestedAmount, PaymentPluginStatus.PENDING);
+        Assert.assertNotNull(pendingPayment);
+        Assert.assertEquals(pendingPayment.getExternalKey(), paymentExternalKey);
+        Assert.assertEquals(pendingPayment.getTransactions().size(), 1);
+        Assert.assertEquals(pendingPayment.getTransactions().get(0).getAmount().compareTo(requestedAmount), 0);
+        Assert.assertEquals(pendingPayment.getTransactions().get(0).getProcessedAmount().compareTo(requestedAmount), 0);
+        Assert.assertEquals(pendingPayment.getTransactions().get(0).getCurrency(), account.getCurrency());
+        Assert.assertEquals(pendingPayment.getTransactions().get(0).getExternalKey(), paymentTransactionExternalKey);
+        Assert.assertEquals(pendingPayment.getTransactions().get(0).getTransactionStatus(), TransactionStatus.PENDING);
+
+        final Payment pendingPayment2 = createPayment(transactionType, pendingPayment.getId(), paymentExternalKey, paymentTransactionExternalKey, pendingAmount, PaymentPluginStatus.PENDING);
+        Assert.assertNotNull(pendingPayment2);
+        Assert.assertEquals(pendingPayment2.getExternalKey(), paymentExternalKey);
+        Assert.assertEquals(pendingPayment2.getTransactions().size(), 1);
+        Assert.assertEquals(pendingPayment2.getTransactions().get(0).getAmount().compareTo(requestedAmount), 0);
+        Assert.assertEquals(pendingPayment2.getTransactions().get(0).getProcessedAmount().compareTo(pendingAmount == null ? requestedAmount : pendingAmount), 0);
+        Assert.assertEquals(pendingPayment2.getTransactions().get(0).getCurrency(), account.getCurrency());
+        Assert.assertEquals(pendingPayment2.getTransactions().get(0).getExternalKey(), paymentTransactionExternalKey);
+        Assert.assertEquals(pendingPayment2.getTransactions().get(0).getTransactionStatus(), TransactionStatus.PENDING);
+
+        final Payment completedPayment = createPayment(transactionType, pendingPayment.getId(), paymentExternalKey, paymentTransactionExternalKey, pendingAmount, PaymentPluginStatus.PROCESSED);
+        Assert.assertNotNull(completedPayment);
+        Assert.assertEquals(completedPayment.getExternalKey(), paymentExternalKey);
+        Assert.assertEquals(completedPayment.getTransactions().size(), 1);
+        Assert.assertEquals(completedPayment.getTransactions().get(0).getAmount().compareTo(requestedAmount), 0);
+        Assert.assertEquals(completedPayment.getTransactions().get(0).getProcessedAmount().compareTo(pendingAmount == null ? requestedAmount : pendingAmount), 0);
+        Assert.assertEquals(completedPayment.getTransactions().get(0).getCurrency(), account.getCurrency());
+        Assert.assertEquals(completedPayment.getTransactions().get(0).getExternalKey(), paymentTransactionExternalKey);
+        Assert.assertEquals(completedPayment.getTransactions().get(0).getTransactionStatus(), TransactionStatus.SUCCESS);
+
+        return completedPayment;
+    }
+
+    private Payment createPayment(final TransactionType transactionType,
+                                  @Nullable final UUID paymentId,
+                                  @Nullable final String paymentExternalKey,
+                                  @Nullable final String paymentTransactionExternalKey,
+                                  @Nullable final BigDecimal amount,
+                                  final PaymentPluginStatus paymentPluginStatus) throws PaymentApiException {
+        final Iterable<PluginProperty> pluginProperties = ImmutableList.<PluginProperty>of(new PluginProperty(MockPaymentProviderPlugin.PLUGIN_PROPERTY_PAYMENT_PLUGIN_STATUS_OVERRIDE, paymentPluginStatus.toString(), false));
+        switch (transactionType) {
+            case AUTHORIZE:
+                return paymentApi.createAuthorization(account,
+                                                      account.getPaymentMethodId(),
+                                                      paymentId,
+                                                      amount,
+                                                      amount == null ? null : account.getCurrency(),
+                                                      paymentExternalKey,
+                                                      paymentTransactionExternalKey,
+                                                      pluginProperties,
+                                                      callContext);
+            case PURCHASE:
+                return paymentApi.createPurchase(account,
+                                                 account.getPaymentMethodId(),
+                                                 paymentId,
+                                                 amount,
+                                                 amount == null ? null : account.getCurrency(),
+                                                 paymentExternalKey,
+                                                 paymentTransactionExternalKey,
+                                                 pluginProperties,
+                                                 callContext);
+            case CREDIT:
+                return paymentApi.createCredit(account,
+                                               account.getPaymentMethodId(),
+                                               paymentId,
+                                               amount,
+                                               amount == null ? null : account.getCurrency(),
+                                               paymentExternalKey,
+                                               paymentTransactionExternalKey,
+                                               pluginProperties,
+                                               callContext);
+            default:
+                Assert.fail();
+                return null;
+        }
     }
 
     private List<PluginProperty> createPropertiesForInvoice(final Invoice invoice) {
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 0d3e7ce..bd323a4 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
@@ -20,6 +20,7 @@ package org.killbill.billing.payment.provider;
 
 import java.math.BigDecimal;
 import java.util.ArrayList;
+import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
 import java.util.UUID;
@@ -55,6 +56,8 @@ import com.google.inject.Inject;
  */
 public class MockPaymentProviderPlugin implements NoOpPaymentPluginApi {
 
+    public static final String PLUGIN_PROPERTY_PAYMENT_PLUGIN_STATUS_OVERRIDE = "paymentPluginStatusOverride";
+
     public static final String PLUGIN_NAME = "__NO_OP__";
 
     private final AtomicBoolean makeNextInvoiceFailWithError = new AtomicBoolean(false);
@@ -208,30 +211,30 @@ public class MockPaymentProviderPlugin implements NoOpPaymentPluginApi {
     @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 {
-        return getPaymentTransactionInfoPluginResult(kbPaymentId, kbTransactionId, TransactionType.AUTHORIZE, amount, currency);
+        return getPaymentTransactionInfoPluginResult(kbPaymentId, kbTransactionId, TransactionType.AUTHORIZE, amount, currency, properties);
     }
 
     @Override
     public PaymentTransactionInfoPlugin capturePayment(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 {
-        return getPaymentTransactionInfoPluginResult(kbPaymentId, kbTransactionId, TransactionType.CAPTURE, amount, currency);
+        return getPaymentTransactionInfoPluginResult(kbPaymentId, kbTransactionId, TransactionType.CAPTURE, amount, currency, properties);
     }
 
     @Override
     public PaymentTransactionInfoPlugin purchasePayment(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 {
-        return getPaymentTransactionInfoPluginResult(kbPaymentId, kbTransactionId, TransactionType.PURCHASE, amount, currency);
+        return getPaymentTransactionInfoPluginResult(kbPaymentId, kbTransactionId, TransactionType.PURCHASE, amount, currency, properties);
     }
 
     @Override
     public PaymentTransactionInfoPlugin voidPayment(final UUID kbAccountId, final UUID kbPaymentId, final UUID kbTransactionId, final UUID kbPaymentMethodId, final Iterable<PluginProperty> properties, final CallContext context)
             throws PaymentPluginApiException {
-        return getPaymentTransactionInfoPluginResult(kbPaymentId, kbTransactionId, TransactionType.VOID, BigDecimal.ZERO, null);
+        return getPaymentTransactionInfoPluginResult(kbPaymentId, kbTransactionId, TransactionType.VOID, BigDecimal.ZERO, null, properties);
     }
 
     @Override
     public PaymentTransactionInfoPlugin creditPayment(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 {
-        return getPaymentTransactionInfoPluginResult(kbPaymentId, kbTransactionId, TransactionType.CREDIT, amount, currency);
+        return getPaymentTransactionInfoPluginResult(kbPaymentId, kbTransactionId, TransactionType.CREDIT, amount, currency, properties);
     }
 
     @Override
@@ -325,23 +328,33 @@ public class MockPaymentProviderPlugin implements NoOpPaymentPluginApi {
             throw new PaymentPluginApiException("", String.format("Refund amount of %s for payment id %s is bigger than the payment amount %s (plugin %s)",
                                                                   refundAmount, kbPaymentId.toString(), maxAmountRefundable, PLUGIN_NAME));
         }
-        return getPaymentTransactionInfoPluginResult(kbPaymentId, kbTransactionId, TransactionType.REFUND, refundAmount, currency);
+        return getPaymentTransactionInfoPluginResult(kbPaymentId, kbTransactionId, TransactionType.REFUND, refundAmount, currency, properties);
     }
 
-    private PaymentTransactionInfoPlugin getPaymentTransactionInfoPluginResult(final UUID kbPaymentId, final UUID kbTransactionId, final TransactionType type, final BigDecimal amount, final Currency currency) throws PaymentPluginApiException {
-
+    private PaymentTransactionInfoPlugin getPaymentTransactionInfoPluginResult(final UUID kbPaymentId, final UUID kbTransactionId, final TransactionType type, final BigDecimal amount, final Currency currency, final Iterable<PluginProperty> pluginProperties) throws PaymentPluginApiException {
         if (makeNextInvoiceFailWithException.getAndSet(false)) {
             throw new PaymentPluginApiException("", "test error");
         }
 
-        final PaymentPluginStatus status = (makeAllInvoicesFailWithError.get() || makeNextInvoiceFailWithError.getAndSet(false)) ? PaymentPluginStatus.ERROR : PaymentPluginStatus.PROCESSED;
+        final PluginProperty paymentPluginStatusOverride = Iterables.tryFind(pluginProperties, new Predicate<PluginProperty>() {
+            @Override
+            public boolean apply(final PluginProperty input) {
+                return PLUGIN_PROPERTY_PAYMENT_PLUGIN_STATUS_OVERRIDE.equals(input.getKey());
+            }
+        }).orNull();
+
+        final PaymentPluginStatus status;
+        if (paymentPluginStatusOverride != null && paymentPluginStatusOverride.getValue() != null) {
+            status = PaymentPluginStatus.valueOf(paymentPluginStatusOverride.getValue().toString());
+        } else {
+            status = (makeAllInvoicesFailWithError.get() || makeNextInvoiceFailWithError.getAndSet(false)) ? PaymentPluginStatus.ERROR : PaymentPluginStatus.PROCESSED;
+        }
 
         InternalPaymentInfo info = payments.get(kbPaymentId.toString());
         if (info == null) {
             info = new InternalPaymentInfo();
             payments.put(kbPaymentId.toString(), info);
         }
-        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());
@@ -350,7 +363,17 @@ public class MockPaymentProviderPlugin implements NoOpPaymentPluginApi {
             paymentTransactions.put(kbPaymentId.toString(), existingTransactions);
         }
 
+        final Iterator<PaymentTransactionInfoPlugin> iterator = existingTransactions.iterator();
+        while (iterator.hasNext()) {
+            final PaymentTransactionInfoPlugin existingTransaction = iterator.next();
+            if (existingTransaction.getKbTransactionPaymentId().equals(kbTransactionId)) {
+                info.addAmount(type, existingTransaction.getAmount().negate());
+                iterator.remove();
+            }
+        }
         existingTransactions.add(result);
+        info.addAmount(type, result.getAmount());
+
         return result;
     }
 }