Details
diff --git a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestInvoicePayment.java b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestInvoicePayment.java
index 2f714dd..e27495f 100644
--- a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestInvoicePayment.java
+++ b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestInvoicePayment.java
@@ -226,7 +226,7 @@ public class TestInvoicePayment extends TestIntegrationBase {
// Trigger chargeback
payment1 = createChargeBackAndCheckForCompletion(account, payment1, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
- Assert.assertEquals(payment1.getPurchasedAmount().compareTo(BigDecimal.TEN), 0);
+ Assert.assertEquals(payment1.getPurchasedAmount().compareTo(BigDecimal.ZERO), 0);
Assert.assertEquals(payment1.getTransactions().size(), 2);
Assert.assertEquals(payment1.getTransactions().get(0).getAmount().compareTo(new BigDecimal("249.95")), 0);
Assert.assertEquals(payment1.getTransactions().get(0).getProcessedAmount().compareTo(BigDecimal.TEN), 0);
@@ -321,7 +321,7 @@ public class TestInvoicePayment extends TestIntegrationBase {
// Trigger chargeback in the original currency
payment1 = createChargeBackAndCheckForCompletion(account, payment1, new BigDecimal("225.44"), Currency.EUR, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
- Assert.assertEquals(payment1.getPurchasedAmount().compareTo(new BigDecimal("249.95")), 0);
+ Assert.assertEquals(payment1.getPurchasedAmount().compareTo(new BigDecimal("24.51")), 0);
Assert.assertEquals(payment1.getTransactions().size(), 2);
Assert.assertEquals(payment1.getTransactions().get(0).getAmount().compareTo(new BigDecimal("249.95")), 0);
Assert.assertEquals(payment1.getTransactions().get(0).getCurrency(), Currency.USD);
diff --git a/payment/src/main/java/org/killbill/billing/payment/api/DefaultPayment.java b/payment/src/main/java/org/killbill/billing/payment/api/DefaultPayment.java
index 1e7962a..33b8730 100644
--- a/payment/src/main/java/org/killbill/billing/payment/api/DefaultPayment.java
+++ b/payment/src/main/java/org/killbill/billing/payment/api/DefaultPayment.java
@@ -19,6 +19,7 @@ package org.killbill.billing.payment.api;
import java.math.BigDecimal;
import java.util.Collection;
+import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.UUID;
@@ -79,9 +80,11 @@ public class DefaultPayment extends EntityBase implements Payment {
}
}
+ final BigDecimal chargebackAmount = getChargebackAmount(transactions);
+
this.authAmount = getAmountForType(nonVoidedTransactions, TransactionType.AUTHORIZE);
- this.captureAmount = getAmountForType(nonVoidedTransactions, TransactionType.CAPTURE);
- this.purchasedAmount = getAmountForType(nonVoidedTransactions, TransactionType.PURCHASE);
+ this.captureAmount = getAmountForType(nonVoidedTransactions, TransactionType.CAPTURE).add(chargebackAmount.negate()).max(BigDecimal.ZERO);
+ this.purchasedAmount = getAmountForType(nonVoidedTransactions, TransactionType.PURCHASE).add(chargebackAmount.negate()).max(BigDecimal.ZERO);
this.creditAmount = getAmountForType(nonVoidedTransactions, TransactionType.CREDIT);
this.refundAmount = getAmountForType(nonVoidedTransactions, TransactionType.REFUND);
@@ -96,6 +99,27 @@ public class DefaultPayment extends EntityBase implements Payment {
this.currency = !transactions.isEmpty() ? transactions.get(0).getCurrency() : null;
}
+ private static BigDecimal getChargebackAmount(final Iterable<PaymentTransaction> transactions) {
+ final Collection<String> successfulChargebackExternalKeys = new HashSet<String>();
+
+ for (final PaymentTransaction transaction : transactions) {
+ // We are looking for the last chargeback in state SUCCESS for a given external key
+ if (TransactionType.CHARGEBACK.equals(transaction.getTransactionType()) && TransactionStatus.SUCCESS.equals(transaction.getTransactionStatus())) {
+ successfulChargebackExternalKeys.add(transaction.getExternalKey());
+ } else if (TransactionType.CHARGEBACK.equals(transaction.getTransactionType()) && TransactionStatus.PAYMENT_FAILURE.equals(transaction.getTransactionStatus())) {
+ successfulChargebackExternalKeys.remove(transaction.getExternalKey());
+ }
+ }
+
+ return getAmountForType(Iterables.<PaymentTransaction>filter(transactions, new Predicate<PaymentTransaction>() {
+ @Override
+ public boolean apply(final PaymentTransaction input) {
+ return successfulChargebackExternalKeys.contains(input.getExternalKey());
+ }
+ }),
+ TransactionType.CHARGEBACK);
+ }
+
private static BigDecimal getAmountForType(final Iterable<PaymentTransaction> transactions, final TransactionType transactiontype) {
BigDecimal result = BigDecimal.ZERO;
BigDecimal processedResult = BigDecimal.ZERO;
diff --git a/payment/src/main/java/org/killbill/billing/payment/api/DefaultPaymentApi.java b/payment/src/main/java/org/killbill/billing/payment/api/DefaultPaymentApi.java
index 385f2e2..9f2d5b6 100644
--- a/payment/src/main/java/org/killbill/billing/payment/api/DefaultPaymentApi.java
+++ b/payment/src/main/java/org/killbill/billing/payment/api/DefaultPaymentApi.java
@@ -1,6 +1,6 @@
/*
- * Copyright 2014-2015 Groupon, Inc
- * Copyright 2014-2015 The Billing Project, LLC
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 The Billing Project, LLC
*
* The Billing Project licenses this file to you under the Apache License, version 2.0
* (the "License"); you may not use this file except in compliance with the
@@ -45,6 +45,7 @@ import org.slf4j.LoggerFactory;
import com.google.common.base.Joiner;
import com.google.common.base.Predicate;
import com.google.common.collect.Iterables;
+import com.google.common.collect.Lists;
public class DefaultPaymentApi extends DefaultApiBase implements PaymentApi {
@@ -663,6 +664,46 @@ public class DefaultPaymentApi extends DefaultApiBase implements PaymentApi {
}
}
+ //@Override TODO 0.17
+ public Payment createChargebackReversal(final Account account, final UUID paymentId, final String paymentTransactionExternalKey, final CallContext callContext) throws PaymentApiException {
+ checkNotNullParameter(account, "account");
+ checkNotNullParameter(paymentId, "paymentId");
+ checkNotNullParameter(paymentTransactionExternalKey, "paymentTransactionExternalKey");
+
+ final String transactionType = TransactionType.CHARGEBACK.name();
+ Payment payment = null;
+ PaymentTransaction paymentTransaction = null;
+ try {
+ logEnterAPICall(transactionType, account, null, paymentId, null, null, null, null, paymentTransactionExternalKey, null, null);
+
+ final InternalCallContext internalCallContext = internalCallContextFactory.createInternalCallContext(account.getId(), callContext);
+ payment = paymentProcessor.createChargebackReversal(IS_API_PAYMENT, NULL_ATTEMPT_ID, account, paymentId, paymentTransactionExternalKey, null, null, true, callContext, internalCallContext);
+
+ // See https://github.com/killbill/killbill/issues/552
+ paymentTransaction = Iterables.<PaymentTransaction>find(Lists.<PaymentTransaction>reverse(payment.getTransactions()),
+ new Predicate<PaymentTransaction>() {
+ @Override
+ public boolean apply(final PaymentTransaction input) {
+ return paymentTransactionExternalKey.equals(input.getExternalKey());
+ }
+ });
+
+ return payment;
+ } finally {
+ logExitAPICall(transactionType,
+ account,
+ payment != null ? payment.getPaymentMethodId() : null,
+ payment != null ? payment.getId() : null,
+ paymentTransaction != null ? paymentTransaction.getId() : null,
+ paymentTransaction != null ? paymentTransaction.getProcessedAmount() : null,
+ paymentTransaction != null ? paymentTransaction.getProcessedCurrency() : null,
+ payment != null ? payment.getExternalKey() : null,
+ paymentTransaction != null ? paymentTransaction.getExternalKey() : null,
+ paymentTransaction != null ? paymentTransaction.getTransactionStatus() : null,
+ null);
+ }
+ }
+
@Override
public List<Payment> getAccountPayments(final UUID accountId, final boolean withPluginInfo, final Iterable<PluginProperty> properties, final TenantContext tenantContext) throws PaymentApiException {
return paymentProcessor.getAccountPayments(accountId, withPluginInfo, tenantContext, internalCallContextFactory.createInternalTenantContext(accountId, tenantContext));
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 1a92573..e4d9edf 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
@@ -1,7 +1,7 @@
/*
* Copyright 2010-2013 Ning, Inc.
- * Copyright 2014-2015 Groupon, Inc
- * Copyright 2014-2015 The Billing Project, LLC
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 The Billing Project, LLC
*
* The Billing Project licenses this file to you under the Apache License, version 2.0
* (the "License"); you may not use this file except in compliance with the
@@ -145,6 +145,11 @@ public class PaymentProcessor extends ProcessorBase {
return performOperation(isApiPayment, attemptId, TransactionType.CHARGEBACK, account, null, paymentId, null, amount, currency, null, paymentTransactionExternalKey, shouldLockAccountAndDispatch, null, PLUGIN_PROPERTIES, callContext, internalCallContext);
}
+ public Payment createChargebackReversal(final boolean isApiPayment, @Nullable final UUID attemptId, final Account account, final UUID paymentId, @Nullable final String paymentTransactionExternalKey, final BigDecimal amount, final Currency currency, final boolean shouldLockAccountAndDispatch,
+ final CallContext callContext, final InternalCallContext internalCallContext) throws PaymentApiException {
+ return performOperation(isApiPayment, attemptId, TransactionType.CHARGEBACK, account, null, paymentId, null, amount, currency, null, paymentTransactionExternalKey, shouldLockAccountAndDispatch, OperationResult.FAILURE, PLUGIN_PROPERTIES, callContext, internalCallContext);
+ }
+
public Payment notifyPendingPaymentOfStateChanged(final Account account, final UUID transactionId, final boolean isSuccess, final CallContext callContext, final InternalCallContext internalCallContext) throws PaymentApiException {
final PaymentTransactionModelDao transactionModelDao = paymentDao.getPaymentTransaction(transactionId, internalCallContext);
if (transactionModelDao.getTransactionStatus() != TransactionStatus.PENDING) {
diff --git a/payment/src/main/java/org/killbill/billing/payment/core/sm/payments/ChargebackInitiated.java b/payment/src/main/java/org/killbill/billing/payment/core/sm/payments/ChargebackInitiated.java
index d1e02c3..0b08255 100644
--- a/payment/src/main/java/org/killbill/billing/payment/core/sm/payments/ChargebackInitiated.java
+++ b/payment/src/main/java/org/killbill/billing/payment/core/sm/payments/ChargebackInitiated.java
@@ -1,6 +1,6 @@
/*
- * Copyright 2014 Groupon, Inc
- * Copyright 2014 The Billing Project, LLC
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 The Billing Project, LLC
*
* The Billing Project licenses this file to you under the Apache License, version 2.0
* (the "License"); you may not use this file except in compliance with the
@@ -17,13 +17,50 @@
package org.killbill.billing.payment.core.sm.payments;
+import org.killbill.automaton.OperationResult;
+import org.killbill.billing.ErrorCode;
import org.killbill.billing.payment.api.PaymentApiException;
import org.killbill.billing.payment.core.sm.PaymentAutomatonDAOHelper;
import org.killbill.billing.payment.core.sm.PaymentStateContext;
+import org.killbill.billing.payment.dao.PaymentTransactionModelDao;
+
+import com.google.common.base.Predicate;
+import com.google.common.collect.Iterables;
public class ChargebackInitiated extends PaymentLeavingStateCallback {
public ChargebackInitiated(final PaymentAutomatonDAOHelper daoHelper, final PaymentStateContext paymentStateContext) throws PaymentApiException {
super(daoHelper, paymentStateContext);
}
+
+ @Override
+ protected void validatePaymentIdAndTransactionType(final Iterable<PaymentTransactionModelDao> existingPaymentTransactions) throws PaymentApiException {
+ if (OperationResult.FAILURE.equals(paymentStateContext.getOverridePluginOperationResult()) && !existingPaymentTransactions.iterator().hasNext()) {
+ // Chargeback reversals can only happen after a successful chargeback
+ throw new PaymentApiException(ErrorCode.PAYMENT_NO_SUCH_SUCCESS_PAYMENT, paymentStateContext.getPaymentId());
+ }
+ super.validatePaymentIdAndTransactionType(existingPaymentTransactions);
+ }
+
+ @Override
+ protected void validateUniqueTransactionExternalKey(final Iterable<PaymentTransactionModelDao> existingPaymentTransactions) throws PaymentApiException {
+ // If no key specified, system will allocate a unique one later, there is nothing to check
+ if (paymentStateContext.getPaymentTransactionExternalKey() == null) {
+ return;
+ }
+
+ // The main difference with the default implementation is that an existing transaction in a SUCCESS state can exist (chargeback reversal)
+ if (Iterables.any(existingPaymentTransactions, new Predicate<PaymentTransactionModelDao>() {
+ @Override
+ public boolean apply(final PaymentTransactionModelDao input) {
+ // An existing transaction for a different payment (to do really well, we should also check on paymentExternalKey which is not available here)
+ return (paymentStateContext.getPaymentId() != null && input.getPaymentId().compareTo(paymentStateContext.getPaymentId()) != 0) ||
+ // Or, an existing transaction for a different account.
+ (!input.getAccountRecordId().equals(paymentStateContext.getInternalCallContext().getAccountRecordId()));
+
+ }
+ })) {
+ throw new PaymentApiException(ErrorCode.PAYMENT_ACTIVE_TRANSACTION_KEY_EXISTS, paymentStateContext.getPaymentTransactionExternalKey());
+ }
+ }
}
diff --git a/payment/src/main/java/org/killbill/billing/payment/core/sm/PaymentStateMachineHelper.java b/payment/src/main/java/org/killbill/billing/payment/core/sm/PaymentStateMachineHelper.java
index dd94638..dd4f298 100644
--- a/payment/src/main/java/org/killbill/billing/payment/core/sm/PaymentStateMachineHelper.java
+++ b/payment/src/main/java/org/killbill/billing/payment/core/sm/PaymentStateMachineHelper.java
@@ -214,7 +214,7 @@ public class PaymentStateMachineHelper {
// A better way would be to change the xml to add attributes to the state (e.g isTerminal, isSuccess, isInit,...)
public boolean isSuccessState(final String stateName) {
- return stateName.endsWith("SUCCESS");
+ return stateName.endsWith("SUCCESS") || stateName.startsWith("CHARGEBACK");
}
public final State fetchNextState(final String prevStateName, final boolean isSuccess) throws MissingEntryException {
diff --git a/payment/src/main/resources/org/killbill/billing/payment/PaymentStates.xml b/payment/src/main/resources/org/killbill/billing/payment/PaymentStates.xml
index 7453caf..da851a2 100644
--- a/payment/src/main/resources/org/killbill/billing/payment/PaymentStates.xml
+++ b/payment/src/main/resources/org/killbill/billing/payment/PaymentStates.xml
@@ -379,7 +379,6 @@
<stateMachine name="CHARGEBACK">
<states>
<state name="CHARGEBACK_INIT"/>
- <state name="CHARGEBACK_PENDING"/>
<state name="CHARGEBACK_SUCCESS"/>
<state name="CHARGEBACK_FAILED"/>
<state name="CHARGEBACK_ERRORED"/>
@@ -400,30 +399,6 @@
<transition>
<initialState>CHARGEBACK_INIT</initialState>
<operation>OP_CHARGEBACK</operation>
- <operationResult>PENDING</operationResult>
- <finalState>CHARGEBACK_PENDING</finalState>
- </transition>
- <transition>
- <initialState>CHARGEBACK_PENDING</initialState>
- <operation>OP_CHARGEBACK</operation>
- <operationResult>SUCCESS</operationResult>
- <finalState>CHARGEBACK_SUCCESS</finalState>
- </transition>
- <transition>
- <initialState>CHARGEBACK_PENDING</initialState>
- <operation>OP_CHARGEBACK</operation>
- <operationResult>FAILURE</operationResult>
- <finalState>CHARGEBACK_FAILED</finalState>
- </transition>
- <transition>
- <initialState>CHARGEBACK_PENDING</initialState>
- <operation>OP_CHARGEBACK</operation>
- <operationResult>EXCEPTION</operationResult>
- <finalState>CHARGEBACK_ERRORED</finalState>
- </transition>
- <transition>
- <initialState>CHARGEBACK_INIT</initialState>
- <operation>OP_CHARGEBACK</operation>
<operationResult>EXCEPTION</operationResult>
<finalState>CHARGEBACK_ERRORED</finalState>
</transition>
@@ -555,5 +530,11 @@
<finalStateMachine>CHARGEBACK</finalStateMachine>
<finalState>CHARGEBACK_INIT</finalState>
</linkStateMachine>
+ <linkStateMachine>
+ <initialStateMachine>CHARGEBACK</initialStateMachine>
+ <initialState>CHARGEBACK_FAILED</initialState>
+ <finalStateMachine>REFUND</finalStateMachine>
+ <finalState>REFUND_INIT</finalState>
+ </linkStateMachine>
</linkStateMachines>
</stateMachineConfig>
diff --git a/payment/src/test/java/org/killbill/billing/payment/api/TestDefaultPayment.java b/payment/src/test/java/org/killbill/billing/payment/api/TestDefaultPayment.java
new file mode 100644
index 0000000..7cf7830
--- /dev/null
+++ b/payment/src/test/java/org/killbill/billing/payment/api/TestDefaultPayment.java
@@ -0,0 +1,194 @@
+/*
+ * Copyright 2016 Groupon, Inc
+ * Copyright 2016 The Billing Project, LLC
+ *
+ * The Billing Project licenses this file to you under the Apache License, version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.killbill.billing.payment.api;
+
+import java.math.BigDecimal;
+import java.util.List;
+import java.util.UUID;
+
+import org.killbill.billing.catalog.api.Currency;
+import org.killbill.billing.payment.PaymentTestSuiteNoDB;
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+import com.google.common.collect.ImmutableList;
+
+public class TestDefaultPayment extends PaymentTestSuiteNoDB {
+
+ @Test(groups = "fast")
+ public void testAmountsCaptureVoided() throws Exception {
+ final UUID paymentId = UUID.randomUUID();
+ final String chargebackExternalKey = UUID.randomUUID().toString();
+ final List<PaymentTransaction> transactions = ImmutableList.<PaymentTransaction>of(buildPaymentTransaction(paymentId, UUID.randomUUID().toString(), TransactionType.AUTHORIZE, TransactionStatus.SUCCESS, BigDecimal.TEN),
+ buildPaymentTransaction(paymentId, UUID.randomUUID().toString(), TransactionType.CAPTURE, TransactionStatus.SUCCESS, BigDecimal.TEN),
+ buildPaymentTransaction(paymentId, UUID.randomUUID().toString(), TransactionType.VOID, TransactionStatus.SUCCESS, null));
+ final Payment payment = buildPayment(paymentId, transactions);
+ Assert.assertEquals(payment.getAuthAmount().compareTo(BigDecimal.TEN), 0);
+ Assert.assertEquals(payment.getCapturedAmount().compareTo(BigDecimal.ZERO), 0);
+ Assert.assertEquals(payment.getPurchasedAmount().compareTo(BigDecimal.ZERO), 0);
+ Assert.assertEquals(payment.getRefundedAmount().compareTo(BigDecimal.ZERO), 0);
+ }
+
+ @Test(groups = "fast")
+ public void testAmountsCaptureVoidedAuthReversed() throws Exception {
+ final UUID paymentId = UUID.randomUUID();
+ final String chargebackExternalKey = UUID.randomUUID().toString();
+ final List<PaymentTransaction> transactions = ImmutableList.<PaymentTransaction>of(buildPaymentTransaction(paymentId, UUID.randomUUID().toString(), TransactionType.AUTHORIZE, TransactionStatus.SUCCESS, BigDecimal.TEN),
+ buildPaymentTransaction(paymentId, UUID.randomUUID().toString(), TransactionType.CAPTURE, TransactionStatus.SUCCESS, BigDecimal.TEN),
+ buildPaymentTransaction(paymentId, UUID.randomUUID().toString(), TransactionType.VOID, TransactionStatus.SUCCESS, null),
+ buildPaymentTransaction(paymentId, UUID.randomUUID().toString(), TransactionType.VOID, TransactionStatus.SUCCESS, null));
+ final Payment payment = buildPayment(paymentId, transactions);
+ Assert.assertEquals(payment.getAuthAmount().compareTo(BigDecimal.ZERO), 0);
+ Assert.assertEquals(payment.getCapturedAmount().compareTo(BigDecimal.ZERO), 0);
+ Assert.assertEquals(payment.getPurchasedAmount().compareTo(BigDecimal.ZERO), 0);
+ Assert.assertEquals(payment.getRefundedAmount().compareTo(BigDecimal.ZERO), 0);
+ }
+
+ @Test(groups = "fast")
+ public void testAmountsCaptureChargeback() throws Exception {
+ final UUID paymentId = UUID.randomUUID();
+ final String chargebackExternalKey = UUID.randomUUID().toString();
+ final List<PaymentTransaction> transactions = ImmutableList.<PaymentTransaction>of(buildPaymentTransaction(paymentId, UUID.randomUUID().toString(), TransactionType.AUTHORIZE, TransactionStatus.SUCCESS, BigDecimal.TEN),
+ buildPaymentTransaction(paymentId, UUID.randomUUID().toString(), TransactionType.CAPTURE, TransactionStatus.SUCCESS, BigDecimal.TEN),
+ buildPaymentTransaction(paymentId, chargebackExternalKey, TransactionType.CHARGEBACK, TransactionStatus.SUCCESS, BigDecimal.TEN));
+ final Payment payment = buildPayment(paymentId, transactions);
+ Assert.assertEquals(payment.getAuthAmount().compareTo(BigDecimal.TEN), 0);
+ Assert.assertEquals(payment.getCapturedAmount().compareTo(BigDecimal.ZERO), 0);
+ Assert.assertEquals(payment.getPurchasedAmount().compareTo(BigDecimal.ZERO), 0);
+ Assert.assertEquals(payment.getRefundedAmount().compareTo(BigDecimal.ZERO), 0);
+ }
+
+ @Test(groups = "fast")
+ public void testAmountsCaptureChargebackReversed() throws Exception {
+ final UUID paymentId = UUID.randomUUID();
+ final String chargebackExternalKey = UUID.randomUUID().toString();
+ final List<PaymentTransaction> transactions = ImmutableList.<PaymentTransaction>of(buildPaymentTransaction(paymentId, UUID.randomUUID().toString(), TransactionType.AUTHORIZE, TransactionStatus.SUCCESS, BigDecimal.TEN),
+ buildPaymentTransaction(paymentId, UUID.randomUUID().toString(), TransactionType.CAPTURE, TransactionStatus.SUCCESS, BigDecimal.TEN),
+ buildPaymentTransaction(paymentId, chargebackExternalKey, TransactionType.CHARGEBACK, TransactionStatus.SUCCESS, BigDecimal.TEN),
+ buildPaymentTransaction(paymentId, chargebackExternalKey, TransactionType.CHARGEBACK, TransactionStatus.PAYMENT_FAILURE, BigDecimal.TEN));
+ final Payment payment = buildPayment(paymentId, transactions);
+ Assert.assertEquals(payment.getAuthAmount().compareTo(BigDecimal.TEN), 0);
+ Assert.assertEquals(payment.getCapturedAmount().compareTo(BigDecimal.TEN), 0);
+ Assert.assertEquals(payment.getPurchasedAmount().compareTo(BigDecimal.ZERO), 0);
+ Assert.assertEquals(payment.getRefundedAmount().compareTo(BigDecimal.ZERO), 0);
+ }
+
+ @Test(groups = "fast")
+ public void testAmountsCaptureChargebackReversedAndRefund() throws Exception {
+ final UUID paymentId = UUID.randomUUID();
+ final String chargebackExternalKey = UUID.randomUUID().toString();
+ final List<PaymentTransaction> transactions = ImmutableList.<PaymentTransaction>of(buildPaymentTransaction(paymentId, UUID.randomUUID().toString(), TransactionType.AUTHORIZE, TransactionStatus.SUCCESS, BigDecimal.TEN),
+ buildPaymentTransaction(paymentId, UUID.randomUUID().toString(), TransactionType.CAPTURE, TransactionStatus.SUCCESS, BigDecimal.TEN),
+ buildPaymentTransaction(paymentId, UUID.randomUUID().toString(), TransactionType.REFUND, TransactionStatus.SUCCESS, BigDecimal.ONE),
+ buildPaymentTransaction(paymentId, chargebackExternalKey, TransactionType.CHARGEBACK, TransactionStatus.SUCCESS, BigDecimal.TEN),
+ buildPaymentTransaction(paymentId, chargebackExternalKey, TransactionType.CHARGEBACK, TransactionStatus.PAYMENT_FAILURE, BigDecimal.TEN));
+ final Payment payment = buildPayment(paymentId, transactions);
+ Assert.assertEquals(payment.getAuthAmount().compareTo(BigDecimal.TEN), 0);
+ Assert.assertEquals(payment.getCapturedAmount().compareTo(BigDecimal.TEN), 0);
+ Assert.assertEquals(payment.getPurchasedAmount().compareTo(BigDecimal.ZERO), 0);
+ Assert.assertEquals(payment.getRefundedAmount().compareTo(BigDecimal.ONE), 0);
+ }
+
+ @Test(groups = "fast")
+ public void testAmountsPurchaseChargeback() throws Exception {
+ final UUID paymentId = UUID.randomUUID();
+ final String chargebackExternalKey = UUID.randomUUID().toString();
+ final List<PaymentTransaction> transactions = ImmutableList.<PaymentTransaction>of(buildPaymentTransaction(paymentId, UUID.randomUUID().toString(), TransactionType.PURCHASE, TransactionStatus.SUCCESS, BigDecimal.TEN),
+ buildPaymentTransaction(paymentId, chargebackExternalKey, TransactionType.CHARGEBACK, TransactionStatus.SUCCESS, BigDecimal.TEN));
+ final Payment payment = buildPayment(paymentId, transactions);
+ Assert.assertEquals(payment.getAuthAmount().compareTo(BigDecimal.ZERO), 0);
+ Assert.assertEquals(payment.getCapturedAmount().compareTo(BigDecimal.ZERO), 0);
+ Assert.assertEquals(payment.getPurchasedAmount().compareTo(BigDecimal.ZERO), 0);
+ Assert.assertEquals(payment.getRefundedAmount().compareTo(BigDecimal.ZERO), 0);
+ }
+
+ @Test(groups = "fast")
+ public void testAmountsPurchaseChargebackReversed() throws Exception {
+ final UUID paymentId = UUID.randomUUID();
+ final String chargebackExternalKey = UUID.randomUUID().toString();
+ final List<PaymentTransaction> transactions = ImmutableList.<PaymentTransaction>of(buildPaymentTransaction(paymentId, UUID.randomUUID().toString(), TransactionType.PURCHASE, TransactionStatus.SUCCESS, BigDecimal.TEN),
+ buildPaymentTransaction(paymentId, chargebackExternalKey, TransactionType.CHARGEBACK, TransactionStatus.SUCCESS, BigDecimal.TEN),
+ buildPaymentTransaction(paymentId, chargebackExternalKey, TransactionType.CHARGEBACK, TransactionStatus.PAYMENT_FAILURE, BigDecimal.TEN));
+ final Payment payment = buildPayment(paymentId, transactions);
+ Assert.assertEquals(payment.getAuthAmount().compareTo(BigDecimal.ZERO), 0);
+ Assert.assertEquals(payment.getCapturedAmount().compareTo(BigDecimal.ZERO), 0);
+ Assert.assertEquals(payment.getPurchasedAmount().compareTo(BigDecimal.TEN), 0);
+ Assert.assertEquals(payment.getRefundedAmount().compareTo(BigDecimal.ZERO), 0);
+ }
+
+ @Test(groups = "fast")
+ public void testAmountsPurchaseChargebackReversedAndRefund() throws Exception {
+ final UUID paymentId = UUID.randomUUID();
+ final String chargebackExternalKey = UUID.randomUUID().toString();
+ final List<PaymentTransaction> transactions = ImmutableList.<PaymentTransaction>of(buildPaymentTransaction(paymentId, UUID.randomUUID().toString(), TransactionType.PURCHASE, TransactionStatus.SUCCESS, BigDecimal.TEN),
+ buildPaymentTransaction(paymentId, UUID.randomUUID().toString(), TransactionType.REFUND, TransactionStatus.SUCCESS, BigDecimal.ONE),
+ buildPaymentTransaction(paymentId, chargebackExternalKey, TransactionType.CHARGEBACK, TransactionStatus.SUCCESS, BigDecimal.TEN),
+ buildPaymentTransaction(paymentId, chargebackExternalKey, TransactionType.CHARGEBACK, TransactionStatus.PAYMENT_FAILURE, BigDecimal.TEN));
+ final Payment payment = buildPayment(paymentId, transactions);
+ Assert.assertEquals(payment.getAuthAmount().compareTo(BigDecimal.ZERO), 0);
+ Assert.assertEquals(payment.getCapturedAmount().compareTo(BigDecimal.ZERO), 0);
+ Assert.assertEquals(payment.getPurchasedAmount().compareTo(BigDecimal.TEN), 0);
+ Assert.assertEquals(payment.getRefundedAmount().compareTo(BigDecimal.ONE), 0);
+ }
+
+ @Test(groups = "fast")
+ public void testAmountsPurchaseMultipleChargebacks() throws Exception {
+ final UUID paymentId = UUID.randomUUID();
+ final String chargebackExternalKey = UUID.randomUUID().toString();
+ final List<PaymentTransaction> transactions = ImmutableList.<PaymentTransaction>of(buildPaymentTransaction(paymentId, UUID.randomUUID().toString(), TransactionType.PURCHASE, TransactionStatus.SUCCESS, BigDecimal.TEN),
+ buildPaymentTransaction(paymentId, UUID.randomUUID().toString(), TransactionType.REFUND, TransactionStatus.SUCCESS, BigDecimal.ONE),
+ buildPaymentTransaction(paymentId, chargebackExternalKey, TransactionType.CHARGEBACK, TransactionStatus.SUCCESS, BigDecimal.ONE),
+ buildPaymentTransaction(paymentId, UUID.randomUUID().toString(), TransactionType.CHARGEBACK, TransactionStatus.SUCCESS, BigDecimal.ONE),
+ buildPaymentTransaction(paymentId, chargebackExternalKey, TransactionType.CHARGEBACK, TransactionStatus.PAYMENT_FAILURE, BigDecimal.ONE));
+ final Payment payment = buildPayment(paymentId, transactions);
+ Assert.assertEquals(payment.getAuthAmount().compareTo(BigDecimal.ZERO), 0);
+ Assert.assertEquals(payment.getCapturedAmount().compareTo(BigDecimal.ZERO), 0);
+ Assert.assertEquals(payment.getPurchasedAmount().compareTo(new BigDecimal("9")), 0);
+ Assert.assertEquals(payment.getRefundedAmount().compareTo(BigDecimal.ONE), 0);
+ }
+
+ private PaymentTransaction buildPaymentTransaction(final UUID paymentId, final String externalKey, final TransactionType transactionType, final TransactionStatus transactionStatus, final BigDecimal amount) {
+ return new DefaultPaymentTransaction(UUID.randomUUID(),
+ UUID.randomUUID(),
+ externalKey,
+ clock.getUTCNow(),
+ clock.getUTCNow(),
+ paymentId,
+ transactionType,
+ clock.getUTCNow(),
+ transactionStatus,
+ amount,
+ Currency.USD,
+ amount,
+ Currency.USD,
+ null,
+ null,
+ null);
+ }
+
+ private Payment buildPayment(final UUID paymentId, final List<PaymentTransaction> transactions) {
+ return new DefaultPayment(paymentId,
+ clock.getUTCNow(),
+ clock.getUTCNow(),
+ UUID.randomUUID(),
+ UUID.randomUUID(),
+ 1,
+ UUID.randomUUID().toString(),
+ transactions);
+ }
+}
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 ac5ec07..a00a2c2 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
@@ -1046,51 +1046,304 @@ public class TestPaymentApi extends PaymentTestSuiteWithEmbeddedDB {
assertEquals(payment2.getCurrency(), Currency.USD);
}
- @Test(groups = "slow")
+ @Test(groups = "slow", description = "https://github.com/killbill/killbill/issues/477")
public void testCreateChargeback() throws PaymentApiException {
+ // API change in 0.17
+ final DefaultPaymentApi paymentApi = (DefaultPaymentApi) this.paymentApi;
+
final BigDecimal requestedAmount = BigDecimal.TEN;
+ final Currency currency = Currency.AED;
+ final String paymentExternalKey = UUID.randomUUID().toString();
+ final String purchaseTransactionExternalKey = UUID.randomUUID().toString();
+ final String chargebackTransactionExternalKey = UUID.randomUUID().toString();
+ final ImmutableList<PluginProperty> properties = ImmutableList.<PluginProperty>of();
+
+ final Payment payment = paymentApi.createPurchase(account,
+ account.getPaymentMethodId(),
+ null,
+ requestedAmount,
+ currency,
+ paymentExternalKey,
+ purchaseTransactionExternalKey,
+ properties,
+ callContext);
- final String paymentExternalKey = "couic";
- final String transactionExternalKey = "couac";
- final String transactionExternalKey2 = "couyc";
+ assertEquals(payment.getExternalKey(), paymentExternalKey);
+ assertEquals(payment.getPaymentMethodId(), account.getPaymentMethodId());
+ assertEquals(payment.getAccountId(), account.getId());
+ assertEquals(payment.getAuthAmount().compareTo(BigDecimal.ZERO), 0);
+ assertEquals(payment.getCapturedAmount().compareTo(BigDecimal.ZERO), 0);
+ assertEquals(payment.getPurchasedAmount().compareTo(requestedAmount), 0);
+ assertEquals(payment.getRefundedAmount().compareTo(BigDecimal.ZERO), 0);
+ assertEquals(payment.getCurrency(), currency);
- final Payment payment = paymentApi.createPurchase(account, account.getPaymentMethodId(), null, requestedAmount, Currency.AED, paymentExternalKey, transactionExternalKey,
- ImmutableList.<PluginProperty>of(), callContext);
+ assertEquals(payment.getTransactions().size(), 1);
+ assertEquals(payment.getTransactions().get(0).getExternalKey(), purchaseTransactionExternalKey);
+ assertEquals(payment.getTransactions().get(0).getPaymentId(), payment.getId());
+ assertEquals(payment.getTransactions().get(0).getAmount().compareTo(requestedAmount), 0);
+ assertEquals(payment.getTransactions().get(0).getCurrency(), currency);
+ assertEquals(payment.getTransactions().get(0).getProcessedAmount().compareTo(requestedAmount), 0);
+ assertEquals(payment.getTransactions().get(0).getProcessedCurrency(), currency);
+ assertEquals(payment.getTransactions().get(0).getTransactionStatus(), TransactionStatus.SUCCESS);
+ assertEquals(payment.getTransactions().get(0).getTransactionType(), TransactionType.PURCHASE);
+ assertEquals(payment.getTransactions().get(0).getGatewayErrorMsg(), "");
+ assertEquals(payment.getTransactions().get(0).getGatewayErrorCode(), "");
- paymentApi.createChargeback(account, payment.getId(), requestedAmount, Currency.AED, transactionExternalKey2, callContext);
- final Payment payment2 = paymentApi.getPayment(payment.getId(), false, ImmutableList.<PluginProperty>of(), callContext);
+ assertEquals(paymentDao.getPayment(payment.getId(), internalCallContext).getStateName(), "PURCHASE_SUCCESS");
+ assertEquals(paymentDao.getPayment(payment.getId(), internalCallContext).getLastSuccessStateName(), "PURCHASE_SUCCESS");
+
+ // First chargeback
+ final Payment payment2 = paymentApi.createChargeback(account,
+ payment.getId(),
+ requestedAmount,
+ currency,
+ chargebackTransactionExternalKey,
+ callContext);
assertEquals(payment2.getExternalKey(), paymentExternalKey);
assertEquals(payment2.getPaymentMethodId(), account.getPaymentMethodId());
assertEquals(payment2.getAccountId(), account.getId());
assertEquals(payment2.getAuthAmount().compareTo(BigDecimal.ZERO), 0);
assertEquals(payment2.getCapturedAmount().compareTo(BigDecimal.ZERO), 0);
- assertEquals(payment2.getPurchasedAmount().compareTo(requestedAmount), 0);
+ // Purchase amount zero-ed out
+ assertEquals(payment2.getPurchasedAmount().compareTo(BigDecimal.ZERO), 0);
assertEquals(payment2.getRefundedAmount().compareTo(BigDecimal.ZERO), 0);
- assertEquals(payment2.getCurrency(), Currency.AED);
+ assertEquals(payment2.getCurrency(), currency);
assertEquals(payment2.getTransactions().size(), 2);
- assertEquals(payment2.getTransactions().get(1).getExternalKey(), transactionExternalKey2);
+ assertEquals(payment2.getTransactions().get(1).getExternalKey(), chargebackTransactionExternalKey);
assertEquals(payment2.getTransactions().get(1).getPaymentId(), payment.getId());
assertEquals(payment2.getTransactions().get(1).getAmount().compareTo(requestedAmount), 0);
- assertEquals(payment2.getTransactions().get(1).getCurrency(), Currency.AED);
-
+ assertEquals(payment2.getTransactions().get(1).getCurrency(), currency);
assertEquals(payment2.getTransactions().get(1).getProcessedAmount().compareTo(requestedAmount), 0);
- assertEquals(payment2.getTransactions().get(1).getProcessedCurrency(), Currency.AED);
-
+ assertEquals(payment2.getTransactions().get(1).getProcessedCurrency(), currency);
assertEquals(payment2.getTransactions().get(1).getTransactionStatus(), TransactionStatus.SUCCESS);
assertEquals(payment2.getTransactions().get(1).getTransactionType(), TransactionType.CHARGEBACK);
assertNull(payment2.getTransactions().get(1).getGatewayErrorMsg());
assertNull(payment2.getTransactions().get(1).getGatewayErrorCode());
- // Attempt to any other operation afterwards, that should fail
+ assertEquals(paymentDao.getPayment(payment.getId(), internalCallContext).getStateName(), "CHARGEBACK_SUCCESS");
+ assertEquals(paymentDao.getPayment(payment.getId(), internalCallContext).getLastSuccessStateName(), "CHARGEBACK_SUCCESS");
+
try {
- paymentApi.createPurchase(account, account.getPaymentMethodId(), payment.getId(), requestedAmount, Currency.AED, paymentExternalKey, transactionExternalKey,
- ImmutableList.<PluginProperty>of(), callContext);
- Assert.fail("Purchase not succeed after a chargeback");
+ paymentApi.createRefund(account,
+ payment.getId(),
+ requestedAmount,
+ currency,
+ UUID.randomUUID().toString(),
+ properties,
+ callContext);
+ Assert.fail("Refunds are no longer permitted after a chargeback");
} catch (final PaymentApiException e) {
- Assert.assertTrue(true);
+ assertEquals(e.getCode(), ErrorCode.PAYMENT_INVALID_OPERATION.getCode());
}
+
+ // First reversal
+ final Payment payment3 = paymentApi.createChargebackReversal(account,
+ payment.getId(),
+ chargebackTransactionExternalKey,
+ callContext);
+
+ assertEquals(payment3.getExternalKey(), paymentExternalKey);
+ assertEquals(payment3.getPaymentMethodId(), account.getPaymentMethodId());
+ assertEquals(payment3.getAccountId(), account.getId());
+ assertEquals(payment3.getAuthAmount().compareTo(BigDecimal.ZERO), 0);
+ assertEquals(payment3.getCapturedAmount().compareTo(BigDecimal.ZERO), 0);
+ // Actual purchase amount
+ assertEquals(payment3.getPurchasedAmount().compareTo(requestedAmount), 0);
+ assertEquals(payment3.getRefundedAmount().compareTo(BigDecimal.ZERO), 0);
+ assertEquals(payment3.getCurrency(), currency);
+
+ assertEquals(payment3.getTransactions().size(), 3);
+ assertEquals(payment3.getTransactions().get(2).getExternalKey(), chargebackTransactionExternalKey);
+ assertEquals(payment3.getTransactions().get(2).getPaymentId(), payment.getId());
+ assertNull(payment3.getTransactions().get(2).getAmount());
+ assertNull(payment3.getTransactions().get(2).getCurrency());
+ assertEquals(payment3.getTransactions().get(2).getProcessedAmount().compareTo(BigDecimal.ZERO), 0);
+ assertNull(payment3.getTransactions().get(2).getProcessedCurrency());
+ assertEquals(payment3.getTransactions().get(2).getTransactionStatus(), TransactionStatus.PAYMENT_FAILURE);
+ assertEquals(payment3.getTransactions().get(2).getTransactionType(), TransactionType.CHARGEBACK);
+ assertNull(payment3.getTransactions().get(2).getGatewayErrorMsg());
+ assertNull(payment3.getTransactions().get(2).getGatewayErrorCode());
+
+ assertEquals(paymentDao.getPayment(payment.getId(), internalCallContext).getStateName(), "CHARGEBACK_FAILED");
+ assertEquals(paymentDao.getPayment(payment.getId(), internalCallContext).getLastSuccessStateName(), "CHARGEBACK_FAILED");
+
+ // Attempt a refund
+ final BigDecimal refundAmount = BigDecimal.ONE;
+ final String refundTransactionExternalKey = UUID.randomUUID().toString();
+ final Payment payment4 = paymentApi.createRefund(account,
+ payment.getId(),
+ refundAmount,
+ currency,
+ refundTransactionExternalKey,
+ properties,
+ callContext);
+
+ assertEquals(payment4.getExternalKey(), paymentExternalKey);
+ assertEquals(payment4.getPaymentMethodId(), account.getPaymentMethodId());
+ assertEquals(payment4.getAccountId(), account.getId());
+ assertEquals(payment4.getAuthAmount().compareTo(BigDecimal.ZERO), 0);
+ assertEquals(payment4.getCapturedAmount().compareTo(BigDecimal.ZERO), 0);
+ // Actual purchase amount
+ assertEquals(payment4.getPurchasedAmount().compareTo(requestedAmount), 0);
+ assertEquals(payment4.getRefundedAmount().compareTo(refundAmount), 0);
+ assertEquals(payment4.getCurrency(), currency);
+
+ assertEquals(payment4.getTransactions().size(), 4);
+ assertEquals(payment4.getTransactions().get(3).getExternalKey(), refundTransactionExternalKey);
+ assertEquals(payment4.getTransactions().get(3).getPaymentId(), payment.getId());
+ assertEquals(payment4.getTransactions().get(3).getAmount().compareTo(refundAmount), 0);
+ assertEquals(payment4.getTransactions().get(3).getCurrency(), currency);
+ assertEquals(payment4.getTransactions().get(3).getProcessedAmount().compareTo(refundAmount), 0);
+ assertEquals(payment4.getTransactions().get(3).getProcessedCurrency(), currency);
+ assertEquals(payment4.getTransactions().get(3).getTransactionStatus(), TransactionStatus.SUCCESS);
+ assertEquals(payment4.getTransactions().get(3).getTransactionType(), TransactionType.REFUND);
+ assertEquals(payment4.getTransactions().get(3).getGatewayErrorMsg(), "");
+ assertEquals(payment4.getTransactions().get(3).getGatewayErrorCode(), "");
+
+ assertEquals(paymentDao.getPayment(payment.getId(), internalCallContext).getStateName(), "REFUND_SUCCESS");
+ assertEquals(paymentDao.getPayment(payment.getId(), internalCallContext).getLastSuccessStateName(), "REFUND_SUCCESS");
+
+ // Second chargeback
+ final BigDecimal secondChargebackAmount = requestedAmount.add(refundAmount.negate());
+ final Payment payment5 = paymentApi.createChargeback(account,
+ payment.getId(),
+ secondChargebackAmount,
+ currency,
+ chargebackTransactionExternalKey,
+ callContext);
+
+ assertEquals(payment5.getExternalKey(), paymentExternalKey);
+ assertEquals(payment5.getPaymentMethodId(), account.getPaymentMethodId());
+ assertEquals(payment5.getAccountId(), account.getId());
+ assertEquals(payment5.getAuthAmount().compareTo(BigDecimal.ZERO), 0);
+ assertEquals(payment5.getCapturedAmount().compareTo(BigDecimal.ZERO), 0);
+ // Purchase amount zero-ed out
+ assertEquals(payment5.getPurchasedAmount().compareTo(BigDecimal.ZERO), 0);
+ assertEquals(payment5.getRefundedAmount().compareTo(refundAmount), 0);
+ assertEquals(payment5.getCurrency(), currency);
+
+ assertEquals(payment5.getTransactions().size(), 5);
+ assertEquals(payment5.getTransactions().get(4).getExternalKey(), chargebackTransactionExternalKey);
+ assertEquals(payment5.getTransactions().get(4).getPaymentId(), payment.getId());
+ assertEquals(payment5.getTransactions().get(4).getAmount().compareTo(secondChargebackAmount), 0);
+ assertEquals(payment5.getTransactions().get(4).getCurrency(), currency);
+ assertEquals(payment5.getTransactions().get(4).getProcessedAmount().compareTo(secondChargebackAmount), 0);
+ assertEquals(payment5.getTransactions().get(4).getProcessedCurrency(), currency);
+ assertEquals(payment5.getTransactions().get(4).getTransactionStatus(), TransactionStatus.SUCCESS);
+ assertEquals(payment5.getTransactions().get(4).getTransactionType(), TransactionType.CHARGEBACK);
+ assertNull(payment5.getTransactions().get(4).getGatewayErrorMsg());
+ assertNull(payment5.getTransactions().get(4).getGatewayErrorCode());
+
+ assertEquals(paymentDao.getPayment(payment.getId(), internalCallContext).getStateName(), "CHARGEBACK_SUCCESS");
+ assertEquals(paymentDao.getPayment(payment.getId(), internalCallContext).getLastSuccessStateName(), "CHARGEBACK_SUCCESS");
+
+ try {
+ paymentApi.createRefund(account,
+ payment.getId(),
+ refundAmount,
+ currency,
+ UUID.randomUUID().toString(),
+ properties,
+ callContext);
+ Assert.fail("Refunds are no longer permitted after a chargeback");
+ } catch (final PaymentApiException e) {
+ assertEquals(e.getCode(), ErrorCode.PAYMENT_INVALID_OPERATION.getCode());
+ }
+
+ // Second reversal
+ final Payment payment6 = paymentApi.createChargebackReversal(account,
+ payment.getId(),
+ chargebackTransactionExternalKey,
+ callContext);
+
+ assertEquals(payment6.getExternalKey(), paymentExternalKey);
+ assertEquals(payment6.getPaymentMethodId(), account.getPaymentMethodId());
+ assertEquals(payment6.getAccountId(), account.getId());
+ assertEquals(payment6.getAuthAmount().compareTo(BigDecimal.ZERO), 0);
+ assertEquals(payment6.getCapturedAmount().compareTo(BigDecimal.ZERO), 0);
+ // Actual purchase amount
+ assertEquals(payment6.getPurchasedAmount().compareTo(requestedAmount), 0);
+ assertEquals(payment6.getRefundedAmount().compareTo(refundAmount), 0);
+ assertEquals(payment6.getCurrency(), currency);
+
+ assertEquals(payment6.getTransactions().size(), 6);
+ assertEquals(payment6.getTransactions().get(5).getExternalKey(), chargebackTransactionExternalKey);
+ assertEquals(payment6.getTransactions().get(5).getPaymentId(), payment.getId());
+ assertNull(payment6.getTransactions().get(5).getAmount());
+ assertNull(payment6.getTransactions().get(5).getCurrency());
+ assertEquals(payment6.getTransactions().get(5).getProcessedAmount().compareTo(BigDecimal.ZERO), 0);
+ assertNull(payment6.getTransactions().get(5).getProcessedCurrency());
+ assertEquals(payment6.getTransactions().get(5).getTransactionStatus(), TransactionStatus.PAYMENT_FAILURE);
+ assertEquals(payment6.getTransactions().get(5).getTransactionType(), TransactionType.CHARGEBACK);
+ assertNull(payment6.getTransactions().get(5).getGatewayErrorMsg());
+ assertNull(payment6.getTransactions().get(5).getGatewayErrorCode());
+
+ assertEquals(paymentDao.getPayment(payment.getId(), internalCallContext).getStateName(), "CHARGEBACK_FAILED");
+ assertEquals(paymentDao.getPayment(payment.getId(), internalCallContext).getLastSuccessStateName(), "CHARGEBACK_FAILED");
+ }
+
+ @Test(groups = "slow")
+ public void testCreateChargebackReversalBeforeChargeback() throws PaymentApiException {
+ // API change in 0.17
+ final DefaultPaymentApi paymentApi = (DefaultPaymentApi) this.paymentApi;
+
+ final BigDecimal requestedAmount = BigDecimal.TEN;
+ final Currency currency = Currency.AED;
+ final String paymentExternalKey = UUID.randomUUID().toString();
+ final String purchaseTransactionExternalKey = UUID.randomUUID().toString();
+ final String chargebackTransactionExternalKey = UUID.randomUUID().toString();
+ final ImmutableList<PluginProperty> properties = ImmutableList.<PluginProperty>of();
+
+ final Payment payment = paymentApi.createPurchase(account,
+ account.getPaymentMethodId(),
+ null,
+ requestedAmount,
+ currency,
+ paymentExternalKey,
+ purchaseTransactionExternalKey,
+ properties,
+ callContext);
+
+ assertEquals(payment.getExternalKey(), paymentExternalKey);
+ assertEquals(payment.getPaymentMethodId(), account.getPaymentMethodId());
+ assertEquals(payment.getAccountId(), account.getId());
+ assertEquals(payment.getAuthAmount().compareTo(BigDecimal.ZERO), 0);
+ assertEquals(payment.getCapturedAmount().compareTo(BigDecimal.ZERO), 0);
+ assertEquals(payment.getPurchasedAmount().compareTo(requestedAmount), 0);
+ assertEquals(payment.getRefundedAmount().compareTo(BigDecimal.ZERO), 0);
+ assertEquals(payment.getCurrency(), currency);
+
+ assertEquals(payment.getTransactions().size(), 1);
+ assertEquals(payment.getTransactions().get(0).getExternalKey(), purchaseTransactionExternalKey);
+ assertEquals(payment.getTransactions().get(0).getPaymentId(), payment.getId());
+ assertEquals(payment.getTransactions().get(0).getAmount().compareTo(requestedAmount), 0);
+ assertEquals(payment.getTransactions().get(0).getCurrency(), currency);
+ assertEquals(payment.getTransactions().get(0).getProcessedAmount().compareTo(requestedAmount), 0);
+ assertEquals(payment.getTransactions().get(0).getProcessedCurrency(), currency);
+ assertEquals(payment.getTransactions().get(0).getTransactionStatus(), TransactionStatus.SUCCESS);
+ assertEquals(payment.getTransactions().get(0).getTransactionType(), TransactionType.PURCHASE);
+ assertEquals(payment.getTransactions().get(0).getGatewayErrorMsg(), "");
+ assertEquals(payment.getTransactions().get(0).getGatewayErrorCode(), "");
+
+ assertEquals(paymentDao.getPayment(payment.getId(), internalCallContext).getStateName(), "PURCHASE_SUCCESS");
+ assertEquals(paymentDao.getPayment(payment.getId(), internalCallContext).getLastSuccessStateName(), "PURCHASE_SUCCESS");
+
+ try {
+ paymentApi.createChargebackReversal(account,
+ payment.getId(),
+ chargebackTransactionExternalKey,
+ callContext);
+ Assert.fail("Chargeback reversals are not permitted before a chargeback");
+ } catch (final PaymentApiException e) {
+ assertEquals(e.getCode(), ErrorCode.PAYMENT_NO_SUCH_SUCCESS_PAYMENT.getCode());
+ }
+
+ assertEquals(paymentApi.getPayment(payment.getId(), false, ImmutableList.<PluginProperty>of(), callContext).getTransactions().size(), 1);
+
+ assertEquals(paymentDao.getPayment(payment.getId(), internalCallContext).getStateName(), "PURCHASE_SUCCESS");
+ assertEquals(paymentDao.getPayment(payment.getId(), internalCallContext).getLastSuccessStateName(), "PURCHASE_SUCCESS");
}
@Test(groups = "slow")