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;
}
}