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 2aabf93..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
@@ -17,6 +17,7 @@
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;
@@ -33,6 +34,15 @@ public class ChargebackInitiated extends PaymentLeavingStateCallback {
}
@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) {
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 fa221bc..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
@@ -1285,6 +1285,68 @@ public class TestPaymentApi extends PaymentTestSuiteWithEmbeddedDB {
}
@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")
public void testNotifyPendingTransactionOfStateChanged() throws PaymentApiException {
final BigDecimal authAmount = BigDecimal.TEN;