killbill-memoizeit

Fix refund code: - Add 2 InvoicePaymentControlPluginApi

6/25/2014 12:07:20 AM

Details

diff --git a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestIntegrationBase.java b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestIntegrationBase.java
index 93ebb57..2c686a5 100644
--- a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestIntegrationBase.java
+++ b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestIntegrationBase.java
@@ -20,10 +20,8 @@ package org.killbill.billing.beatrix.integration;
 
 import java.math.BigDecimal;
 import java.util.ArrayList;
-import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
-import java.util.Set;
 import java.util.UUID;
 
 import javax.annotation.Nullable;
@@ -127,6 +125,7 @@ public class TestIntegrationBase extends BeatrixTestSuiteWithEmbeddedDB {
         public boolean isExternalPayment() {
             return false;
         }
+
         @Override
         public String getPaymentControlPluginName() {
             return InvoicePaymentControlPluginApi.PLUGIN_NAME;
@@ -138,6 +137,7 @@ public class TestIntegrationBase extends BeatrixTestSuiteWithEmbeddedDB {
         public boolean isExternalPayment() {
             return true;
         }
+
         @Override
         public String getPaymentControlPluginName() {
             return InvoicePaymentControlPluginApi.PLUGIN_NAME;
@@ -403,90 +403,103 @@ public class TestIntegrationBase extends BeatrixTestSuiteWithEmbeddedDB {
         }, events);
     }
 
-    protected void createPaymentAndCheckForCompletion(final Account account, final Invoice invoice, final NextEvent... events) {
-        doCallAndCheckForCompletion(new Function<Void, Void>() {
+    protected DirectPayment createPaymentAndCheckForCompletion(final Account account, final Invoice invoice, final BigDecimal amount, final Currency currency,  final NextEvent... events) {
+        return doCallAndCheckForCompletion(new Function<Void, DirectPayment>() {
             @Override
-            public Void apply(@Nullable final Void input) {
+            public DirectPayment apply(@Nullable final Void input) {
                 try {
-                    paymentApi.createPurchaseWithPaymentControl(account, account.getPaymentMethodId(), null, invoice.getBalance(), invoice.getCurrency(), invoice.getId().toString(),
-                                                                UUID.randomUUID().toString(), PLUGIN_PROPERTIES, PAYMENT_OPTIONS, callContext);
+                    return paymentApi.createPurchaseWithPaymentControl(account, account.getPaymentMethodId(), null, amount, currency, invoice.getId().toString(),
+                                                                       UUID.randomUUID().toString(), PLUGIN_PROPERTIES, PAYMENT_OPTIONS, callContext);
                 } catch (final PaymentApiException e) {
                     fail(e.toString());
+                    return null;
                 }
-                return null;
             }
         }, events);
     }
 
-    protected void createExternalPaymentAndCheckForCompletion(final Account account, final Invoice invoice, final NextEvent... events) {
-        doCallAndCheckForCompletion(new Function<Void, Void>() {
+    protected DirectPayment createPaymentAndCheckForCompletion(final Account account, final Invoice invoice, final NextEvent... events) {
+        return doCallAndCheckForCompletion(new Function<Void, DirectPayment>() {
             @Override
-            public Void apply(@Nullable final Void input) {
+            public DirectPayment apply(@Nullable final Void input) {
                 try {
-                    paymentApi.createPurchaseWithPaymentControl(account, account.getPaymentMethodId(), null, invoice.getBalance(), invoice.getCurrency(), invoice.getId().toString(),
-                                                                UUID.randomUUID().toString(), PLUGIN_PROPERTIES, EXTERNAL_PAYMENT_OPTIONS, callContext);
+                    return paymentApi.createPurchaseWithPaymentControl(account, account.getPaymentMethodId(), null, invoice.getBalance(), invoice.getCurrency(), invoice.getId().toString(),
+                                                                       UUID.randomUUID().toString(), PLUGIN_PROPERTIES, PAYMENT_OPTIONS, callContext);
                 } catch (final PaymentApiException e) {
                     fail(e.toString());
+                    return null;
                 }
-                return null;
             }
         }, events);
     }
 
+    protected DirectPayment createExternalPaymentAndCheckForCompletion(final Account account, final Invoice invoice, final NextEvent... events) {
+        return doCallAndCheckForCompletion(new Function<Void, DirectPayment>() {
+            @Override
+            public DirectPayment apply(@Nullable final Void input) {
+                try {
+                    return paymentApi.createPurchaseWithPaymentControl(account, account.getPaymentMethodId(), null, invoice.getBalance(), invoice.getCurrency(), invoice.getId().toString(),
+                                                                       UUID.randomUUID().toString(), PLUGIN_PROPERTIES, EXTERNAL_PAYMENT_OPTIONS, callContext);
+                } catch (final PaymentApiException e) {
+                    fail(e.toString());
+                    return null;
+                }
+            }
+        }, events);
+    }
 
-    protected void refundPaymentAndCheckForCompletion(final Account account, final DirectPayment payment, final NextEvent... events) {
-        doCallAndCheckForCompletion(new Function<Void, Void>() {
+    protected DirectPayment refundPaymentAndCheckForCompletion(final Account account, final DirectPayment payment, final NextEvent... events) {
+        return doCallAndCheckForCompletion(new Function<Void, DirectPayment>() {
             @Override
-            public Void apply(@Nullable final Void input) {
+            public DirectPayment apply(@Nullable final Void input) {
                 try {
-                    paymentApi.createRefundWithPaymentControl(account, payment.getId(), payment.getPurchasedAmount(), payment.getCurrency(), UUID.randomUUID().toString(),
-                                                              PLUGIN_PROPERTIES, PAYMENT_OPTIONS, callContext);
+                    return paymentApi.createRefundWithPaymentControl(account, payment.getId(), payment.getPurchasedAmount(), payment.getCurrency(), UUID.randomUUID().toString(),
+                                                                     PLUGIN_PROPERTIES, PAYMENT_OPTIONS, callContext);
                 } catch (final PaymentApiException e) {
                     fail(e.toString());
+                    return null;
                 }
-                return null;
             }
         }, events);
     }
 
-    protected void refundPaymentWithAdjustmentAndCheckForCompletion(final Account account, final DirectPayment payment, final NextEvent... events) {
-        doCallAndCheckForCompletion(new Function<Void, Void>() {
+    protected DirectPayment refundPaymentWithAdjustmentAndCheckForCompletion(final Account account, final DirectPayment payment, final NextEvent... events) {
+        return doCallAndCheckForCompletion(new Function<Void, DirectPayment>() {
             @Override
-            public Void apply(@Nullable final Void input) {
+            public DirectPayment apply(@Nullable final Void input) {
 
-                /*
-                STEPH
+                final List<PluginProperty> properties = new ArrayList<PluginProperty>();
+                final PluginProperty prop1 = new PluginProperty(InvoicePaymentControlPluginApi.PROP_IPCD_REFUND_WITH_ADJUSTMENTS, "true", false);
+                properties.add(prop1);
                 try {
-                    paymentApi.createRefundWithAdjustment(account, payment.getId(), payment.getCapturedAmount()  TODO [PAYMENT] payment.getPaidAmount() , PLUGIN_PROPERTIES, callContext);
+                    return paymentApi.createRefundWithPaymentControl(account, payment.getId(), payment.getPurchasedAmount(), payment.getCurrency(), UUID.randomUUID().toString(),
+                                                                     properties, PAYMENT_OPTIONS, callContext);
                 } catch (final PaymentApiException e) {
                     fail(e.toString());
+                    return null;
                 }
-            */
-                return null;
             }
         }, events);
     }
 
-    protected void refundPaymentWithInvoiceItemAdjAndCheckForCompletion(final Account account, final DirectPayment payment, final Set<UUID> invoiceItems, final NextEvent... events) {
-        doCallAndCheckForCompletion(new Function<Void, Void>() {
+    protected DirectPayment refundPaymentWithInvoiceItemAdjAndCheckForCompletion(final Account account, final DirectPayment payment, final Map<UUID, BigDecimal> iias, final NextEvent... events) {
+        return doCallAndCheckForCompletion(new Function<Void, DirectPayment>() {
             @Override
-            public Void apply(@Nullable final Void input) {
+            public DirectPayment apply(@Nullable final Void input) {
 
-                final Map<UUID, BigDecimal> invoiceItemsWithAmount = new HashMap<UUID, BigDecimal>();
-                for (UUID cur : invoiceItems) {
-                    invoiceItemsWithAmount.put(cur, null);
-                }
                 final List<PluginProperty> properties = new ArrayList<PluginProperty>();
-                final PluginProperty prop = new PluginProperty(InvoicePaymentControlPluginApi.IPCD_REFUND_IDS_WITH_AMOUNT_KEY,invoiceItemsWithAmount, false);
-                properties.add(prop);
+                final PluginProperty prop1 = new PluginProperty(InvoicePaymentControlPluginApi.PROP_IPCD_REFUND_WITH_ADJUSTMENTS, "true", false);
+                properties.add(prop1);
+                final PluginProperty prop2 = new PluginProperty(InvoicePaymentControlPluginApi.PROP_IPCD_REFUND_IDS_WITH_AMOUNT_KEY, iias, false);
+                properties.add(prop2);
 
                 try {
-                    paymentApi.createRefundWithPaymentControl(account, payment.getId(), payment.getPurchasedAmount(), payment.getCurrency(), UUID.randomUUID().toString(),
-                                                              properties, PAYMENT_OPTIONS, callContext);
+                    return paymentApi.createRefundWithPaymentControl(account, payment.getId(), payment.getPurchasedAmount(), payment.getCurrency(), UUID.randomUUID().toString(),
+                                                                     properties, PAYMENT_OPTIONS, callContext);
                 } catch (final PaymentApiException e) {
                     fail(e.toString());
+                    return null;
                 }
-                return null;
             }
         }, events);
     }
diff --git a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestIntegrationInvoiceWithRepairLogic.java b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestIntegrationInvoiceWithRepairLogic.java
index 13a38f2..580fd2b 100644
--- a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestIntegrationInvoiceWithRepairLogic.java
+++ b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestIntegrationInvoiceWithRepairLogic.java
@@ -546,10 +546,7 @@ public class TestIntegrationInvoiceWithRepairLogic extends TestIntegrationBase {
 
         final Map<UUID, BigDecimal> iias = new HashMap<UUID, BigDecimal>();
         iias.put(invoice1.getInvoiceItems().get(0).getId(), new BigDecimal("197.26"));
-        busHandler.pushExpectedEvents(NextEvent.INVOICE_ADJUSTMENT);
-        // STEPH paymentApi.createRefundWithItemsAdjustments(account, payment1.getId(), iias, PLUGIN_PROPERTIES, callContext);
-        assertListenerStatus();
-
+        refundPaymentWithInvoiceItemAdjAndCheckForCompletion(account, payment1, iias, NextEvent.PAYMENT, NextEvent.INVOICE_ADJUSTMENT);
         checkNoMoreInvoiceToGenerate(account);
     }
 
@@ -617,10 +614,7 @@ public class TestIntegrationInvoiceWithRepairLogic extends TestIntegrationBase {
 
         final Map<UUID, BigDecimal> iias = new HashMap<UUID, BigDecimal>();
         iias.put(invoice1.getInvoiceItems().get(0).getId(), new BigDecimal("100.00"));
-        busHandler.pushExpectedEvents(NextEvent.INVOICE_ADJUSTMENT);
-        // STEPH paymentApi.createRefundWithItemsAdjustments(account, payment1.getId(), iias, PLUGIN_PROPERTIES, callContext);
-        assertListenerStatus();
-
+        refundPaymentWithInvoiceItemAdjAndCheckForCompletion(account, payment1, iias, NextEvent.PAYMENT, NextEvent.INVOICE_ADJUSTMENT);
         checkNoMoreInvoiceToGenerate(account);
     }
 }
diff --git a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestPayment.java b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestPayment.java
index 39ab3a9..89ccf34 100644
--- a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestPayment.java
+++ b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestPayment.java
@@ -53,9 +53,8 @@ public class TestPayment extends TestIntegrationBase {
         final InvoiceItem item1 = invoiceUserApi.insertExternalCharges(account.getId(), clock.getUTCToday(), ImmutableList.<InvoiceItem>of(externalCharge), callContext).get(0);
         assertListenerStatus();
 
-        busHandler.pushExpectedEvents(NextEvent.PAYMENT);
-        final DirectPayment payment1 = null; // STEPH paymentApi.createPurchase(account, item1.getInvoiceId(), new BigDecimal("4.00"), PLUGIN_PROPERTIES, callContext);
-        assertListenerStatus();
+        final Invoice invoice = invoiceUserApi.getInvoice(item1.getInvoiceId(), callContext);
+        final DirectPayment payment1 = createPaymentAndCheckForCompletion(account, invoice, new BigDecimal("4.00"), account.getCurrency(),  NextEvent.PAYMENT);
 
         Invoice invoice1 = invoiceUserApi.getInvoice(item1.getInvoiceId(), callContext);
         assertTrue(invoice1.getBalance().compareTo(new BigDecimal("6.00")) == 0);
@@ -65,9 +64,7 @@ public class TestPayment extends TestIntegrationBase {
         BigDecimal accountBalance = invoiceUserApi.getAccountBalance(account.getId(), callContext);
         assertTrue(accountBalance.compareTo(new BigDecimal("6.00")) == 0);
 
-        busHandler.pushExpectedEvents(NextEvent.PAYMENT);
-        final DirectPayment payment2 = null; // STEPH paymentApi.createPayment(account, item1.getInvoiceId(), new BigDecimal("6.00"), PLUGIN_PROPERTIES, callContext);
-        assertListenerStatus();
+        final DirectPayment payment2 = createPaymentAndCheckForCompletion(account, invoice, new BigDecimal("6.00"), account.getCurrency(),  NextEvent.PAYMENT);
 
         invoice1 = invoiceUserApi.getInvoice(item1.getInvoiceId(), callContext);
         assertTrue(invoice1.getBalance().compareTo(BigDecimal.ZERO) == 0);
@@ -90,14 +87,14 @@ public class TestPayment extends TestIntegrationBase {
         assertTrue(accountBalance.compareTo(BigDecimal.ZERO) == 0);
 
 */
-        // And then issue refund with item adjustment on first invoice/item
-        // STEPH paymentApi.createRefund(account, payment2.getId(), new BigDecimal("5.00"), PLUGIN_PROPERTIES, callContext);
+
+        refundPaymentAndCheckForCompletion(account, payment1, NextEvent.PAYMENT, NextEvent.INVOICE_ADJUSTMENT);
 
         invoice1 = invoiceUserApi.getInvoice(item1.getInvoiceId(), callContext);
-        assertTrue(invoice1.getBalance().compareTo(new BigDecimal("5.00")) == 0);
+        assertTrue(invoice1.getBalance().compareTo(new BigDecimal("4.00")) == 0);
 
         accountBalance = invoiceUserApi.getAccountBalance(account.getId(), callContext);
-        assertTrue(accountBalance.compareTo(new BigDecimal("5.00")) == 0);
+        assertTrue(accountBalance.compareTo(new BigDecimal("4.00")) == 0);
 
     }
 }
diff --git a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestPaymentRefund.java b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestPaymentRefund.java
index d649033..54755ff 100644
--- a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestPaymentRefund.java
+++ b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestPaymentRefund.java
@@ -19,7 +19,9 @@
 package org.killbill.billing.beatrix.integration;
 
 import java.math.BigDecimal;
+import java.util.HashMap;
 import java.util.HashSet;
+import java.util.Map;
 import java.util.Set;
 import java.util.UUID;
 
@@ -71,13 +73,16 @@ public class TestPaymentRefund extends TestIntegrationBase {
     @Test(groups = "slow")
     public void testRefundWithNoAdjustments() throws Exception {
         // Although we don't adjust the invoice, the invoicing system sends an event because invoice balance changes and overdue system-- in particular-- needs to know about it.
-        refundPaymentAndCheckForCompletion(account, payment, NextEvent.INVOICE_ADJUSTMENT);
+        refundPaymentAndCheckForCompletion(account, payment, NextEvent.PAYMENT, NextEvent.INVOICE_ADJUSTMENT);
         refundChecker.checkRefund(payment.getId(), callContext, new ExpectedRefundCheck(payment.getId(), false, new BigDecimal("233.82"), Currency.USD, initialCreationDate.toLocalDate()));
     }
 
     @Test(groups = "slow")
     public void testRefundWithInvoiceItemAdjustemts() throws Exception {
-        refundPaymentWithInvoiceItemAdjAndCheckForCompletion(account, payment, invoiceItems, NextEvent.INVOICE_ADJUSTMENT);
+
+        final Map<UUID, BigDecimal> iias = new HashMap<UUID, BigDecimal>();
+        iias.put(invoice.getInvoiceItems().get(0).getId(), null);
+        refundPaymentWithInvoiceItemAdjAndCheckForCompletion(account, payment, iias, NextEvent.PAYMENT, NextEvent.INVOICE_ADJUSTMENT);
         refundChecker.checkRefund(payment.getId(), callContext, new ExpectedRefundCheck(payment.getId(), true, new BigDecimal("233.82"), Currency.USD, initialCreationDate.toLocalDate()));
         invoice = invoiceChecker.checkInvoice(account.getId(), invoiceItemCount++, callContext,
                                               new ExpectedInvoiceItemCheck(new LocalDate(2012, 3, 2),
@@ -88,7 +93,7 @@ public class TestPaymentRefund extends TestIntegrationBase {
 
     @Test(groups = "slow")
     public void testRefundWithInvoiceAdjustment() throws Exception {
-        refundPaymentWithAdjustmentAndCheckForCompletion(account, payment, NextEvent.INVOICE_ADJUSTMENT);
+        refundPaymentWithAdjustmentAndCheckForCompletion(account, payment, NextEvent.PAYMENT, NextEvent.INVOICE_ADJUSTMENT);
         refundChecker.checkRefund(payment.getId(), callContext, new ExpectedRefundCheck(payment.getId(), true, new BigDecimal("233.82"), Currency.USD, initialCreationDate.toLocalDate()));
         invoice = invoiceChecker.checkInvoice(account.getId(), invoiceItemCount++, callContext,
                                               new ExpectedInvoiceItemCheck(new LocalDate(2012, 3, 2),
diff --git a/beatrix/src/test/java/org/killbill/billing/beatrix/util/RefundChecker.java b/beatrix/src/test/java/org/killbill/billing/beatrix/util/RefundChecker.java
index a5b56c7..323394c 100644
--- a/beatrix/src/test/java/org/killbill/billing/beatrix/util/RefundChecker.java
+++ b/beatrix/src/test/java/org/killbill/billing/beatrix/util/RefundChecker.java
@@ -35,7 +35,9 @@ import org.killbill.billing.payment.api.DirectPayment;
 import org.killbill.billing.payment.api.DirectPaymentApi;
 import org.killbill.billing.payment.api.DirectPaymentTransaction;
 import org.killbill.billing.payment.api.PaymentApiException;
+import org.killbill.billing.payment.api.PluginProperty;
 import org.killbill.billing.payment.api.TransactionType;
+import org.killbill.billing.payment.dao.PluginPropertyModelDao;
 import org.killbill.billing.util.callcontext.CallContext;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -43,6 +45,7 @@ import org.testng.Assert;
 
 import com.google.common.base.Predicate;
 import com.google.common.collect.Collections2;
+import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Iterables;
 import com.google.inject.Inject;
 
@@ -63,26 +66,29 @@ public class RefundChecker {
         this.invoiceUserApi = invoiceApi;
     }
 
-    public DirectPayment checkRefund(final UUID paymentId, final CallContext context, ExpectedRefundCheck expected) throws PaymentApiException {
+    public DirectPaymentTransaction checkRefund(final UUID paymentId, final CallContext context, ExpectedRefundCheck expected) throws PaymentApiException {
 
 
-        final DirectPayment payment = null; // STEPH paymentApi.getPaymentRefunds(paymentId, context);
-        final List<DirectPayment> refunds = null; // STEPH paymentApi.getPaymentRefunds(paymentId, context);
-        Assert.assertEquals(refunds.size(), 1);
+        final DirectPayment payment = paymentApi.getPayment(paymentId, false, ImmutableList.<PluginProperty>of(), context);
+        final DirectPaymentTransaction refund = Iterables.tryFind(payment.getTransactions(), new Predicate<DirectPaymentTransaction>() {
+            @Override
+            public boolean apply(final DirectPaymentTransaction input) {
+                return input.getTransactionType() == TransactionType.REFUND;
+            }
+        }).orNull();
+
+        Assert.assertNotNull(refund);
 
         final InvoicePayment refundInvoicePayment = getInvoicePaymentEntry(paymentId, InvoicePaymentType.REFUND, context);
         final InvoicePayment invoicePayment = getInvoicePaymentEntry(paymentId, InvoicePaymentType.ATTEMPT, context);
 
-        final DirectPayment refund = refunds.get(0);
-        final DirectPaymentTransaction refundTransaction = getRefundTransaction(refund);
-
-        Assert.assertEquals(refundTransaction.getDirectPaymentId(), expected.getPaymentId());
+        Assert.assertEquals(refund.getDirectPaymentId(), expected.getPaymentId());
         Assert.assertEquals(refund.getCurrency(), expected.getCurrency());
-        Assert.assertEquals(refundTransaction.getAmount().compareTo(expected.getRefundAmount()), 0);
+        Assert.assertEquals(refund.getAmount().compareTo(expected.getRefundAmount()), 0);
 
         Assert.assertEquals(refundInvoicePayment.getPaymentId(), paymentId);
         Assert.assertEquals(refundInvoicePayment.getLinkedInvoicePaymentId(), invoicePayment.getId());
-        Assert.assertEquals(refundInvoicePayment.getPaymentCookieId(), refund.getId());
+        Assert.assertEquals(refundInvoicePayment.getPaymentCookieId(), refund.getExternalKey());
         Assert.assertEquals(refundInvoicePayment.getInvoiceId(), invoicePayment.getInvoiceId());
         Assert.assertEquals(refundInvoicePayment.getAmount().compareTo(expected.getRefundAmount().negate()), 0);
         Assert.assertEquals(refundInvoicePayment.getCurrency(), expected.getCurrency());
diff --git a/payment/src/main/java/org/killbill/billing/payment/control/InvoicePaymentControlPluginApi.java b/payment/src/main/java/org/killbill/billing/payment/control/InvoicePaymentControlPluginApi.java
index e0dd294..5cbce7b 100644
--- a/payment/src/main/java/org/killbill/billing/payment/control/InvoicePaymentControlPluginApi.java
+++ b/payment/src/main/java/org/killbill/billing/payment/control/InvoicePaymentControlPluginApi.java
@@ -77,6 +77,7 @@ public final class InvoicePaymentControlPluginApi implements PaymentControlPlugi
     public final static String CREATED_BY = "InvoicePaymentControlPluginApi";
 
     public static final String IPCD_REFUND_IDS_WITH_AMOUNT_KEY = "IPCD_REF_IDS_AMOUNTS";
+    public static final String IPCD_REFUND_WITH_ADJUSTMENTS = "IPCD_REFUND_WITH_ADJUSTMENTS";
 
     private final PaymentConfig paymentConfig;
     private final InvoiceInternalApi invoiceApi;
@@ -126,16 +127,28 @@ public final class InvoicePaymentControlPluginApi implements PaymentControlPlugi
     @Override
     public void onSuccessCall(final PaymentControlContext paymentControlContext) throws PaymentControlApiException {
 
+        final TransactionType transactionType = paymentControlContext.getTransactionType();
+        Preconditions.checkArgument(transactionType == TransactionType.PURCHASE ||
+                                    transactionType == TransactionType.REFUND);
+
         final UUID invoiceId = UUID.fromString(paymentControlContext.getPaymentExternalKey());
         final InternalCallContext internalContext = internalCallContextFactory.createInternalCallContext(paymentControlContext.getAccountId(), paymentControlContext);
         try {
-            invoiceApi.notifyOfPayment(invoiceId,
-                                       paymentControlContext.getAmount(),
-                                       paymentControlContext.getCurrency(),
-                                       paymentControlContext.getProcessedCurrency(),
-                                       paymentControlContext.getPaymentId(),
-                                       paymentControlContext.getCreatedDate(),
-                                       internalContext);
+            if (transactionType == TransactionType.PURCHASE) {
+                invoiceApi.notifyOfPayment(invoiceId,
+                                           paymentControlContext.getAmount(),
+                                           paymentControlContext.getCurrency(),
+                                           paymentControlContext.getProcessedCurrency(),
+                                           paymentControlContext.getPaymentId(),
+                                           paymentControlContext.getCreatedDate(),
+                                           internalContext);
+            } else /* TransactionType.REFUND */ {
+                final Map<UUID, BigDecimal> idWithAmount = extractIdsWithAmountFromProperties(paymentControlContext.getPluginProperties());
+                final PluginProperty prop = getPluginProperty(paymentControlContext.getPluginProperties(), IPCD_REFUND_WITH_ADJUSTMENTS);
+                final boolean isAdjusted = prop != null ? Boolean.valueOf((String) prop.getValue()) : false;
+                invoiceApi.createRefund(paymentControlContext.getPaymentId(), paymentControlContext.getAmount(), isAdjusted , idWithAmount, paymentControlContext.getTransactionExternalKey(), internalContext);
+            }
+
         } catch (InvoiceApiException e) {
             logger.error("Invoice " + invoiceId + " seems missing", e);
             //throw new PaymentControlApiException(e);
@@ -212,36 +225,49 @@ public final class InvoicePaymentControlPluginApi implements PaymentControlPlugi
     }
 
     private Map<UUID, BigDecimal> extractIdsWithAmountFromProperties(final Iterable<PluginProperty> properties) {
-        final PluginProperty prop = Iterables.tryFind(properties, new Predicate<PluginProperty>() {
-            @Override
-            public boolean apply(final PluginProperty input) {
-                return input.getKey().equals(IPCD_REFUND_IDS_WITH_AMOUNT_KEY);
-            }
-        }).orNull();
+        final PluginProperty prop = getPluginProperty(properties, PROP_IPCD_REFUND_IDS_WITH_AMOUNT_KEY);
         if (prop == null) {
             return ImmutableMap.<UUID, BigDecimal>of();
         }
         return (Map<UUID, BigDecimal>) prop.getValue();
     }
 
+    private PluginProperty getPluginProperty(final Iterable<PluginProperty> properties, final String propertyName) {
+        return Iterables.tryFind(properties, new Predicate<PluginProperty>() {
+            @Override
+            public boolean apply(final PluginProperty input) {
+                return input.getKey().equals(propertyName);
+            }
+        }).orNull();
+    }
+
     private BigDecimal computeRefundAmount(final UUID paymentId, @Nullable final BigDecimal specifiedRefundAmount,
                                            final Map<UUID, BigDecimal> invoiceItemIdsWithAmounts, final InternalTenantContext context)
             throws PaymentControlApiException {
+
+        if (invoiceItemIdsWithAmounts.size() == 0) {
+            if (specifiedRefundAmount == null || specifiedRefundAmount.compareTo(BigDecimal.ZERO) <= 0) {
+                throw new PaymentControlApiException("You need to specify positive a refund amount");
+            }
+            return specifiedRefundAmount;
+        }
+
+        // If we have
         final List<InvoiceItem> items;
         try {
             items = invoiceApi.getInvoiceForPaymentId(paymentId, context).getInvoiceItems();
 
             BigDecimal amountFromItems = BigDecimal.ZERO;
             for (final UUID itemId : invoiceItemIdsWithAmounts.keySet()) {
-                amountFromItems = amountFromItems.add(Objects.firstNonNull(invoiceItemIdsWithAmounts.get(itemId),
-                                                                           getAmountFromItem(items, itemId)));
-            }
-            // Sanity check: if some items were specified, then the sum should be equal to specified refund amount, if specified
-            if (amountFromItems.compareTo(BigDecimal.ZERO) != 0 && specifiedRefundAmount != null && specifiedRefundAmount.compareTo(amountFromItems) != 0) {
-                throw new PaymentControlApiException("You can't specify a refund amount that doesn't match the invoice items amounts");
+                final BigDecimal specifiedItemAmount = invoiceItemIdsWithAmounts.get(itemId);
+                final BigDecimal itemAmount = getAmountFromItem(items, itemId);
+                if (specifiedItemAmount != null &&
+                    (specifiedItemAmount.compareTo(BigDecimal.ZERO) <= 0 || specifiedItemAmount.compareTo(itemAmount) > 0)) {
+                    throw new PaymentControlApiException("You need to specify valid invoice item amount ");
+                }
+                amountFromItems = amountFromItems.add(Objects.firstNonNull(specifiedItemAmount, itemAmount));
             }
-
-            return Objects.firstNonNull(specifiedRefundAmount, amountFromItems);
+            return amountFromItems;
         } catch (InvoiceApiException e) {
             throw new PaymentControlApiException(e);
         }
@@ -380,7 +406,7 @@ public final class InvoicePaymentControlPluginApi implements PaymentControlPlugi
     }
 
     private boolean insert_AUTO_PAY_OFF_ifRequired(final PaymentControlContext paymentControlContext) {
-        if (!isAccountAutoPayOff(paymentControlContext.getAccountId(), paymentControlContext)) {
+        if (paymentControlContext.isApiPayment() || !isAccountAutoPayOff(paymentControlContext.getAccountId(), paymentControlContext)) {
             return false;
         }
         final PluginAutoPayOffModelDao data = new PluginAutoPayOffModelDao(paymentControlContext.getPaymentExternalKey(), paymentControlContext.getTransactionExternalKey(), paymentControlContext.getAccountId(), PLUGIN_NAME,
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 283eee5..26eee32 100644
--- a/payment/src/main/resources/org/killbill/billing/payment/PaymentStates.xml
+++ b/payment/src/main/resources/org/killbill/billing/payment/PaymentStates.xml
@@ -308,7 +308,7 @@
         </linkStateMachine>
         <linkStateMachine>
             <initialStateMachine>REFUND</initialStateMachine>
-            <initialState>REFUND_FAILED</initialState>
+            <initialState>REFUND_SUCCESS</initialState>
             <finalStateMachine>REFUND</finalStateMachine>
             <finalState>REFUND_INIT</finalState>
         </linkStateMachine>
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 04880e4..f4b72de 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
@@ -412,7 +412,7 @@ public class TestPaymentApi extends PaymentTestSuiteWithEmbeddedDB {
         final List<PluginProperty> refundProperties = new ArrayList<PluginProperty>();
         final HashMap<UUID, BigDecimal> uuidBigDecimalHashMap = new HashMap<UUID, BigDecimal>();
         uuidBigDecimalHashMap.put(invoiceItem.getId(), null);
-        final PluginProperty refundIdsProp = new PluginProperty(InvoicePaymentControlPluginApi.IPCD_REFUND_IDS_WITH_AMOUNT_KEY, uuidBigDecimalHashMap, false);
+        final PluginProperty refundIdsProp = new PluginProperty(InvoicePaymentControlPluginApi.PROP_IPCD_REFUND_IDS_WITH_AMOUNT_KEY, uuidBigDecimalHashMap, false);
         refundProperties.add(refundIdsProp);
 
         final DirectPayment payment2 = paymentApi.createRefundWithPaymentControl(account, payment.getId(), null, Currency.USD, transactionExternalKey2,