killbill-memoizeit

Merge remote-tracking branch 'origin/fix-for-981' into

6/17/2018 4:47:15 PM

Changes

invoice/src/main/java/org/killbill/billing/invoice/api/invoice/DefaultInvoicePaymentApi.java 70(+0 -70)

invoice/src/test/java/org/killbill/billing/invoice/api/MockInvoicePaymentApi.java 50(+0 -50)

pom.xml 2(+1 -1)

util/src/test/java/org/killbill/billing/mock/glue/MockPaymentModule.java 38(+0 -38)

Details

diff --git a/api/src/main/java/org/killbill/billing/glue/InvoiceModule.java b/api/src/main/java/org/killbill/billing/glue/InvoiceModule.java
index ce939ec..6ebf2b5 100644
--- a/api/src/main/java/org/killbill/billing/glue/InvoiceModule.java
+++ b/api/src/main/java/org/killbill/billing/glue/InvoiceModule.java
@@ -1,7 +1,9 @@
 /*
  * Copyright 2010-2013 Ning, Inc.
+ * Copyright 2014-2018 Groupon, Inc
+ * Copyright 2014-2018 The Billing Project, LLC
  *
- * Ning licenses this file to you under the Apache License, version 2.0
+ * 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:
  *
@@ -22,7 +24,5 @@ public interface InvoiceModule {
 
     public void installInvoiceUserApi();
 
-    public void installInvoicePaymentApi();
-
     public void installInvoiceInternalApi();
 }
diff --git a/api/src/main/java/org/killbill/billing/invoice/api/InvoiceInternalApi.java b/api/src/main/java/org/killbill/billing/invoice/api/InvoiceInternalApi.java
index 8dabb05..ffe6ad5 100644
--- a/api/src/main/java/org/killbill/billing/invoice/api/InvoiceInternalApi.java
+++ b/api/src/main/java/org/killbill/billing/invoice/api/InvoiceInternalApi.java
@@ -1,7 +1,7 @@
 /*
  * Copyright 2010-2011 Ning, Inc.
- * Copyright 2014-2016 Groupon, Inc
- * Copyright 2014-2016 The Billing Project, LLC
+ * Copyright 2014-2018 Groupon, Inc
+ * Copyright 2014-2018 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
@@ -20,6 +20,7 @@ package org.killbill.billing.invoice.api;
 
 import java.math.BigDecimal;
 import java.util.Collection;
+import java.util.List;
 import java.util.Map;
 import java.util.UUID;
 
@@ -28,6 +29,7 @@ import org.joda.time.LocalDate;
 import org.killbill.billing.callcontext.InternalCallContext;
 import org.killbill.billing.callcontext.InternalTenantContext;
 import org.killbill.billing.catalog.api.Currency;
+import org.killbill.billing.util.callcontext.TenantContext;
 
 public interface InvoiceInternalApi {
 
@@ -77,4 +79,10 @@ public interface InvoiceInternalApi {
     public Map<UUID, BigDecimal> validateInvoiceItemAdjustments(final UUID paymentId, final Map<UUID, BigDecimal> idWithAmount, final InternalTenantContext context) throws InvoiceApiException;
 
     public void commitInvoice(UUID invoiceId, InternalCallContext context) throws InvoiceApiException;
+
+    public List<InvoicePayment> getInvoicePayments(UUID paymentId, TenantContext context);
+
+    public List<InvoicePayment> getInvoicePaymentsByAccount(UUID accountId, TenantContext context);
+
+    public InvoicePayment getInvoicePaymentByCookieId(String cookieId, TenantContext context);
 }
diff --git a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/db/TestDBRouter.java b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/db/TestDBRouter.java
index 94cb0ac..6cd9b9b 100644
--- a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/db/TestDBRouter.java
+++ b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/db/TestDBRouter.java
@@ -76,6 +76,10 @@ public class TestDBRouter extends TestIntegrationBase {
 
     @AfterMethod(groups = "slow")
     public void afterMethod() throws Exception {
+        if (hasFailed()) {
+            return;
+        }
+
         externalBus.unregister(publicListener);
         super.afterMethod();
     }
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 1001b44..f992889 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,7 +20,6 @@ package org.killbill.billing.beatrix.integration;
 
 import java.math.BigDecimal;
 import java.util.ArrayList;
-import java.util.Collection;
 import java.util.List;
 import java.util.Map;
 import java.util.UUID;
@@ -72,7 +71,7 @@ import org.killbill.billing.invoice.api.DryRunType;
 import org.killbill.billing.invoice.api.Invoice;
 import org.killbill.billing.invoice.api.InvoiceApiException;
 import org.killbill.billing.invoice.api.InvoiceItem;
-import org.killbill.billing.invoice.api.InvoicePaymentApi;
+import org.killbill.billing.invoice.api.InvoicePayment;
 import org.killbill.billing.invoice.api.InvoiceService;
 import org.killbill.billing.invoice.api.InvoiceUserApi;
 import org.killbill.billing.junction.BlockingInternalApi;
@@ -89,6 +88,7 @@ import org.killbill.billing.overdue.listener.OverdueListener;
 import org.killbill.billing.overdue.wrapper.OverdueWrapper;
 import org.killbill.billing.overdue.wrapper.OverdueWrapperFactory;
 import org.killbill.billing.payment.api.AdminPaymentApi;
+import org.killbill.billing.payment.api.InvoicePaymentApi;
 import org.killbill.billing.payment.api.Payment;
 import org.killbill.billing.payment.api.PaymentApi;
 import org.killbill.billing.payment.api.PaymentApiException;
@@ -519,11 +519,19 @@ public class TestIntegrationBase extends BeatrixTestSuiteWithEmbeddedDB implemen
             @Override
             public Payment apply(@Nullable final Void input) {
                 try {
-                    final List<PluginProperty> properties = new ArrayList<PluginProperty>();
-                    final PluginProperty prop1 = new PluginProperty(InvoicePaymentControlPluginApi.PROP_IPCD_INVOICE_ID, invoice.getId().toString(), false);
-                    properties.add(prop1);
-                    return paymentApi.createPurchaseWithPaymentControl(account, account.getPaymentMethodId(), null, amount, currency, null, UUID.randomUUID().toString(),
-                                                                       UUID.randomUUID().toString(), properties, PAYMENT_OPTIONS, callContext);
+                    final InvoicePayment invoicePayment = invoicePaymentApi.createPurchaseForInvoicePayment(account,
+                                                                                                            invoice.getId(),
+                                                                                                            account.getPaymentMethodId(),
+                                                                                                            null,
+                                                                                                            amount,
+                                                                                                            currency,
+                                                                                                            null,
+                                                                                                            UUID.randomUUID().toString(),
+                                                                                                            UUID.randomUUID().toString(),
+                                                                                                            ImmutableList.<PluginProperty>of(),
+                                                                                                            PAYMENT_OPTIONS,
+                                                                                                            callContext);
+                    return paymentApi.getPayment(invoicePayment.getPaymentId(), false, false, ImmutableList.<PluginProperty>of(), callContext);
                 } catch (final PaymentApiException e) {
                     fail(e.toString());
                     return null;
@@ -533,22 +541,7 @@ public class TestIntegrationBase extends BeatrixTestSuiteWithEmbeddedDB implemen
     }
 
     protected Payment createPaymentAndCheckForCompletion(final Account account, final Invoice invoice, final NextEvent... events) {
-        return doCallAndCheckForCompletion(new Function<Void, Payment>() {
-            @Override
-            public Payment apply(@Nullable final Void input) {
-                try {
-                    final List<PluginProperty> properties = new ArrayList<PluginProperty>();
-                    final PluginProperty prop1 = new PluginProperty(InvoicePaymentControlPluginApi.PROP_IPCD_INVOICE_ID, invoice.getId().toString(), false);
-                    properties.add(prop1);
-
-                    return paymentApi.createPurchaseWithPaymentControl(account, account.getPaymentMethodId(), null, invoice.getBalance(), invoice.getCurrency(), null, UUID.randomUUID().toString(),
-                                                                       UUID.randomUUID().toString(), properties, PAYMENT_OPTIONS, callContext);
-                } catch (final PaymentApiException e) {
-                    fail(e.toString());
-                    return null;
-                }
-            }
-        }, events);
+        return createPaymentAndCheckForCompletion(account, invoice, invoice.getBalance(), invoice.getCurrency(), events);
     }
 
     protected Payment createExternalPaymentAndCheckForCompletion(final Account account, final Invoice invoice, final NextEvent... events) {
@@ -556,13 +549,19 @@ public class TestIntegrationBase extends BeatrixTestSuiteWithEmbeddedDB implemen
             @Override
             public Payment apply(@Nullable final Void input) {
                 try {
-
-                    final List<PluginProperty> properties = new ArrayList<PluginProperty>();
-                    final PluginProperty prop1 = new PluginProperty(InvoicePaymentControlPluginApi.PROP_IPCD_INVOICE_ID, invoice.getId().toString(), false);
-                    properties.add(prop1);
-
-                    return paymentApi.createPurchaseWithPaymentControl(account, account.getPaymentMethodId(), null, invoice.getBalance(), invoice.getCurrency(), null, UUID.randomUUID().toString(),
-                                                                       UUID.randomUUID().toString(), properties, EXTERNAL_PAYMENT_OPTIONS, callContext);
+                    final InvoicePayment invoicePayment = invoicePaymentApi.createPurchaseForInvoicePayment(account,
+                                                                                                            invoice.getId(),
+                                                                                                            account.getPaymentMethodId(),
+                                                                                                            null,
+                                                                                                            invoice.getBalance(),
+                                                                                                            invoice.getCurrency(),
+                                                                                                            null,
+                                                                                                            UUID.randomUUID().toString(),
+                                                                                                            UUID.randomUUID().toString(),
+                                                                                                            ImmutableList.<PluginProperty>of(),
+                                                                                                            EXTERNAL_PAYMENT_OPTIONS,
+                                                                                                            callContext);
+                    return paymentApi.getPayment(invoicePayment.getPaymentId(), false, false, ImmutableList.<PluginProperty>of(), callContext);
                 } catch (final PaymentApiException e) {
                     fail(e.toString());
                     return null;
@@ -599,15 +598,10 @@ public class TestIntegrationBase extends BeatrixTestSuiteWithEmbeddedDB implemen
         return doCallAndCheckForCompletion(new Function<Void, Payment>() {
             @Override
             public Payment apply(@Nullable final Void input) {
-                final Collection<PluginProperty> properties = new ArrayList<PluginProperty>();
-                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 {
-                    return paymentApi.createRefundWithPaymentControl(account, payment.getId(), amount, currency, null, UUID.randomUUID().toString(),
-                                                                     properties, PAYMENT_OPTIONS, callContext);
+                    final InvoicePayment invoicePayment = invoicePaymentApi.createRefundForInvoicePayment(true, iias, account, payment.getId(), amount, currency, null, UUID.randomUUID().toString(),
+                                                                                                          ImmutableList.<PluginProperty>of(), PAYMENT_OPTIONS, callContext);
+                    return paymentApi.getPayment(invoicePayment.getPaymentId(), false, false, ImmutableList.<PluginProperty>of(), callContext);
                 } catch (final PaymentApiException e) {
                     fail(e.toString());
                     return null;
diff --git a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestIntegrationParentInvoice.java b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestIntegrationParentInvoice.java
index 1916de5..d099c50 100644
--- a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestIntegrationParentInvoice.java
+++ b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestIntegrationParentInvoice.java
@@ -18,7 +18,6 @@
 package org.killbill.billing.beatrix.integration;
 
 import java.math.BigDecimal;
-import java.util.ArrayList;
 import java.util.Iterator;
 import java.util.List;
 import java.util.UUID;
@@ -50,7 +49,6 @@ import org.killbill.billing.invoice.notification.ParentInvoiceCommitmentNotifier
 import org.killbill.billing.payment.api.Payment;
 import org.killbill.billing.payment.api.PaymentApiException;
 import org.killbill.billing.payment.api.PluginProperty;
-import org.killbill.billing.payment.invoice.InvoicePaymentControlPluginApi;
 import org.killbill.notificationq.api.NotificationEvent;
 import org.killbill.notificationq.api.NotificationEventWithMetadata;
 import org.killbill.notificationq.api.NotificationQueue;
@@ -1157,17 +1155,23 @@ public class TestIntegrationParentInvoice extends TestIntegrationBase {
         assertEquals(invoiceUserApi.getAccountBalance(parentAccount.getId(), callContext).compareTo(new BigDecimal("249.95")), 0);
         assertEquals(invoiceUserApi.getAccountBalance(childAccount.getId(), callContext).compareTo(new BigDecimal("249.95")), 0);
 
-
         // Verify Invoice apis getParentAccountId/getParentInvoiceId
         assertEquals(childInvoices.get(1).getParentAccountId(), parentAccount.getId());
         assertEquals(childInvoices.get(1).getParentInvoiceId(), parentInvoice.getId());
 
         try {
-            final List<PluginProperty> properties = new ArrayList<PluginProperty>();
-            final PluginProperty prop1 = new PluginProperty(InvoicePaymentControlPluginApi.PROP_IPCD_INVOICE_ID, childInvoices.get(1).getId().toString(), false);
-            properties.add(prop1);
-            paymentApi.createPurchaseWithPaymentControl(childAccount, childAccount.getPaymentMethodId(), null, childInvoices.get(1).getBalance(), childInvoices.get(1).getCurrency(), null, UUID.randomUUID().toString(),
-                                                        UUID.randomUUID().toString(), properties, PAYMENT_OPTIONS, callContext);
+            invoicePaymentApi.createPurchaseForInvoicePayment(childAccount,
+                                                              childInvoices.get(1).getId(),
+                                                              childAccount.getPaymentMethodId(),
+                                                              null,
+                                                              childInvoices.get(1).getBalance(),
+                                                              childInvoices.get(1).getCurrency(),
+                                                              null,
+                                                              UUID.randomUUID().toString(),
+                                                              UUID.randomUUID().toString(),
+                                                              ImmutableList.<PluginProperty>of(),
+                                                              PAYMENT_OPTIONS,
+                                                              callContext);
             Assert.fail("Payment should fail, invoice belongs to parent");
         } catch (final PaymentApiException e) {
             assertEquals(ErrorCode.PAYMENT_PLUGIN_API_ABORTED.getCode(), e.getCode());
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 a8b45dc..b00c5e3 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
@@ -20,7 +20,6 @@ package org.killbill.billing.beatrix.integration;
 import java.io.ByteArrayInputStream;
 import java.io.InputStream;
 import java.math.BigDecimal;
-import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
@@ -1065,12 +1064,19 @@ public class TestInvoicePayment extends TestIntegrationBase {
         // We expect an INVOICE_PAYMENT that indicates the invoice was repaired, and also an exception because plugin aborts payment call since there is nothing to do.
         //
         busHandler.pushExpectedEvents(NextEvent.INVOICE_PAYMENT);
-        final List<PluginProperty> properties = new ArrayList<PluginProperty>();
-        final PluginProperty prop1 = new PluginProperty(InvoicePaymentControlPluginApi.PROP_IPCD_INVOICE_ID, updateInvoice2.getId().toString(), false);
-        properties.add(prop1);
         try {
-            paymentApi.createPurchaseWithPaymentControl(account, account.getPaymentMethodId(), null, updateInvoice2.getBalance(), updateInvoice2.getCurrency(), null, UUID.randomUUID().toString(),
-                                                        UUID.randomUUID().toString(), properties, PAYMENT_OPTIONS, callContext);
+            invoicePaymentApi.createPurchaseForInvoicePayment(account,
+                                                              updateInvoice2.getId(),
+                                                              account.getPaymentMethodId(),
+                                                              null,
+                                                              updateInvoice2.getBalance(),
+                                                              updateInvoice2.getCurrency(),
+                                                              null,
+                                                              UUID.randomUUID().toString(),
+                                                              UUID.randomUUID().toString(),
+                                                              ImmutableList.<PluginProperty>of(),
+                                                              PAYMENT_OPTIONS,
+                                                              callContext);
             Assert.fail("The payment should not succeed (and yet it will repair the broken state....)");
         } catch (final PaymentApiException expected) {
             Assert.assertEquals(expected.getCode(), ErrorCode.PAYMENT_PLUGIN_API_ABORTED.getCode());
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 9b1379b..ad12eae 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
@@ -22,7 +22,6 @@ import java.math.BigDecimal;
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.HashSet;
-import java.util.List;
 import java.util.Map;
 import java.util.Set;
 import java.util.UUID;
@@ -48,7 +47,6 @@ import org.killbill.billing.payment.api.Payment;
 import org.killbill.billing.payment.api.PaymentApiException;
 import org.killbill.billing.payment.api.PluginProperty;
 import org.killbill.billing.payment.api.TransactionStatus;
-import org.killbill.billing.payment.invoice.InvoicePaymentControlPluginApi;
 import org.testng.Assert;
 import org.testng.annotations.BeforeMethod;
 import org.testng.annotations.Test;
@@ -56,8 +54,8 @@ import org.testng.annotations.Test;
 import com.google.common.base.Function;
 import com.google.common.base.Predicate;
 import com.google.common.collect.Collections2;
+import com.google.common.collect.ImmutableList;
 
-import static org.testng.Assert.assertEquals;
 import static org.testng.Assert.assertNotNull;
 import static org.testng.Assert.assertTrue;
 import static org.testng.Assert.fail;
@@ -116,13 +114,9 @@ public class TestPaymentRefund extends TestIntegrationBase {
 
     @Test(groups = "slow")
     public void testFailedRefundWithInvoiceAdjustment() throws Exception {
-
-        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.createRefundWithPaymentControl(account, payment.getId(), payment.getPurchasedAmount(), payment.getCurrency(), null, UUID.randomUUID().toString(),
-                                                             properties, PAYMENT_OPTIONS, callContext);
+            invoicePaymentApi.createRefundForInvoicePayment(true, null, account, payment.getId(), payment.getPurchasedAmount(), payment.getCurrency(), null, UUID.randomUUID().toString(),
+                                                            ImmutableList.<PluginProperty>of(), PAYMENT_OPTIONS, callContext);
             fail("Refund with invoice adjustment should now throw an Exception");
         } catch (final PaymentApiException e) {
             Assert.assertEquals(e.getCause(), null);
diff --git a/beatrix/src/test/java/org/killbill/billing/beatrix/util/PaymentChecker.java b/beatrix/src/test/java/org/killbill/billing/beatrix/util/PaymentChecker.java
index 9925dde..d5975a4 100644
--- a/beatrix/src/test/java/org/killbill/billing/beatrix/util/PaymentChecker.java
+++ b/beatrix/src/test/java/org/killbill/billing/beatrix/util/PaymentChecker.java
@@ -25,7 +25,7 @@ import java.util.UUID;
 import org.joda.time.LocalDate;
 import org.killbill.billing.catalog.api.Currency;
 import org.killbill.billing.invoice.api.InvoicePayment;
-import org.killbill.billing.invoice.api.InvoicePaymentApi;
+import org.killbill.billing.payment.api.InvoicePaymentApi;
 import org.killbill.billing.payment.api.Payment;
 import org.killbill.billing.payment.api.PaymentApi;
 import org.killbill.billing.payment.api.PaymentTransaction;
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 9472f6b..be4df72 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
@@ -28,9 +28,9 @@ import javax.annotation.Nullable;
 import org.joda.time.LocalDate;
 import org.killbill.billing.catalog.api.Currency;
 import org.killbill.billing.invoice.api.InvoicePayment;
-import org.killbill.billing.invoice.api.InvoicePaymentApi;
 import org.killbill.billing.invoice.api.InvoicePaymentType;
 import org.killbill.billing.invoice.api.InvoiceUserApi;
+import org.killbill.billing.payment.api.InvoicePaymentApi;
 import org.killbill.billing.payment.api.Payment;
 import org.killbill.billing.payment.api.PaymentApi;
 import org.killbill.billing.payment.api.PaymentTransaction;
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/api/svcs/DefaultInvoiceInternalApi.java b/invoice/src/main/java/org/killbill/billing/invoice/api/svcs/DefaultInvoiceInternalApi.java
index d98be06..1460429 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/api/svcs/DefaultInvoiceInternalApi.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/api/svcs/DefaultInvoiceInternalApi.java
@@ -29,6 +29,7 @@ import javax.inject.Inject;
 import org.joda.time.DateTime;
 import org.joda.time.LocalDate;
 import org.killbill.billing.ErrorCode;
+import org.killbill.billing.ObjectType;
 import org.killbill.billing.callcontext.InternalCallContext;
 import org.killbill.billing.callcontext.InternalTenantContext;
 import org.killbill.billing.catalog.api.Currency;
@@ -48,6 +49,7 @@ import org.killbill.billing.invoice.model.DefaultInvoicePayment;
 import org.killbill.billing.payment.api.PluginProperty;
 import org.killbill.billing.util.callcontext.CallContext;
 import org.killbill.billing.util.callcontext.InternalCallContextFactory;
+import org.killbill.billing.util.callcontext.TenantContext;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -79,7 +81,7 @@ public class DefaultInvoiceInternalApi implements InvoiceInternalApi {
         return getInvoiceByIdInternal(invoiceId, context);
     }
 
-    private DefaultInvoice getInvoiceByIdInternal(final UUID invoiceId, final InternalTenantContext context)throws InvoiceApiException {
+    private DefaultInvoice getInvoiceByIdInternal(final UUID invoiceId, final InternalTenantContext context) throws InvoiceApiException {
         return new DefaultInvoice(dao.getById(invoiceId, context));
     }
 
@@ -180,7 +182,7 @@ public class DefaultInvoiceInternalApi implements InvoiceInternalApi {
             @Override
             public boolean apply(final InvoicePaymentModelDao input) {
                 return input.getType() == type &&
-                        input.getSuccess();
+                       input.getSuccess();
             }
         }).orNull();
         return resultOrNull != null ? new DefaultInvoicePayment(resultOrNull) : null;
@@ -190,4 +192,34 @@ public class DefaultInvoiceInternalApi implements InvoiceInternalApi {
     public void commitInvoice(final UUID invoiceId, final InternalCallContext context) throws InvoiceApiException {
         dao.changeInvoiceStatus(invoiceId, InvoiceStatus.COMMITTED, context);
     }
+
+    @Override
+    public List<InvoicePayment> getInvoicePayments(final UUID paymentId, final TenantContext context) {
+        return ImmutableList.<InvoicePayment>copyOf(Collections2.transform(dao.getInvoicePaymentsByPaymentId(paymentId, internalCallContextFactory.createInternalTenantContext(paymentId, ObjectType.PAYMENT, context)),
+                                                                           new Function<InvoicePaymentModelDao, InvoicePayment>() {
+                                                                               @Override
+                                                                               public InvoicePayment apply(final InvoicePaymentModelDao input) {
+                                                                                   return new DefaultInvoicePayment(input);
+                                                                               }
+                                                                           }
+                                                                          ));
+    }
+
+    @Override
+    public List<InvoicePayment> getInvoicePaymentsByAccount(final UUID accountId, final TenantContext context) {
+        return ImmutableList.<InvoicePayment>copyOf(Collections2.transform(dao.getInvoicePaymentsByAccount(internalCallContextFactory.createInternalTenantContext(accountId, ObjectType.ACCOUNT, context)),
+                                                                           new Function<InvoicePaymentModelDao, InvoicePayment>() {
+                                                                               @Override
+                                                                               public InvoicePayment apply(final InvoicePaymentModelDao input) {
+                                                                                   return new DefaultInvoicePayment(input);
+                                                                               }
+                                                                           }
+                                                                          ));
+    }
+
+    @Override
+    public InvoicePayment getInvoicePaymentByCookieId(final String cookieId, final TenantContext context) {
+        final InvoicePaymentModelDao invoicePaymentModelDao = dao.getInvoicePaymentByCookieId(cookieId, internalCallContextFactory.createInternalTenantContext(context.getAccountId(), ObjectType.ACCOUNT, context));
+        return new DefaultInvoicePayment(invoicePaymentModelDao);
+    }
 }
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/dao/DefaultInvoiceDao.java b/invoice/src/main/java/org/killbill/billing/invoice/dao/DefaultInvoiceDao.java
index fd7d11a..e40b3b2 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/dao/DefaultInvoiceDao.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/dao/DefaultInvoiceDao.java
@@ -539,6 +539,16 @@ public class DefaultInvoiceDao extends EntityDaoBase<InvoiceModelDao, Invoice, I
     }
 
     @Override
+    public InvoicePaymentModelDao getInvoicePaymentByCookieId(final String cookieId, final InternalTenantContext context) {
+        return transactionalSqlDao.execute(true, new EntitySqlDaoTransactionWrapper<InvoicePaymentModelDao>() {
+            @Override
+            public InvoicePaymentModelDao inTransaction(final EntitySqlDaoWrapperFactory entitySqlDaoWrapperFactory) throws Exception {
+                return entitySqlDaoWrapperFactory.become(InvoicePaymentSqlDao.class).getPaymentForCookieId(cookieId, context);
+            }
+        });
+    }
+
+    @Override
     public InvoicePaymentModelDao createRefund(final UUID paymentId, final BigDecimal requestedRefundAmount, final boolean isInvoiceAdjusted,
                                                final Map<UUID, BigDecimal> invoiceItemIdsWithNullAmounts, final String transactionExternalKey,
                                                final InternalCallContext context) throws InvoiceApiException {
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/dao/InvoiceDao.java b/invoice/src/main/java/org/killbill/billing/invoice/dao/InvoiceDao.java
index 205c55f..67c1d1b 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/dao/InvoiceDao.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/dao/InvoiceDao.java
@@ -64,6 +64,8 @@ public interface InvoiceDao extends EntityDao<InvoiceModelDao, Invoice, InvoiceA
 
     List<InvoicePaymentModelDao> getInvoicePaymentsByAccount(InternalTenantContext context);
 
+    InvoicePaymentModelDao getInvoicePaymentByCookieId(String cookieId, InternalTenantContext internalTenantContext);
+
     BigDecimal getAccountBalance(UUID accountId, InternalTenantContext context);
 
     BigDecimal getAccountCBA(UUID accountId, InternalTenantContext context);
@@ -174,7 +176,6 @@ public interface InvoiceDao extends EntityDao<InvoiceModelDao, Invoice, InvoiceA
      */
     List<InvoiceParentChildModelDao> getChildInvoicesByParentInvoiceId(UUID parentInvoiceId, final InternalCallContext context) throws InvoiceApiException;
 
-
     /**
      * Retrieve parent invoice by the parent account id
      *
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/dao/InvoicePaymentSqlDao.java b/invoice/src/main/java/org/killbill/billing/invoice/dao/InvoicePaymentSqlDao.java
index 994eb58..2723b68 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/dao/InvoicePaymentSqlDao.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/dao/InvoicePaymentSqlDao.java
@@ -1,7 +1,7 @@
 /*
  * Copyright 2010-2013 Ning, Inc.
- * Copyright 2014-2016 Groupon, Inc
- * Copyright 2014-2016 The Billing Project, LLC
+ * Copyright 2014-2018 Groupon, Inc
+ * Copyright 2014-2018 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
@@ -27,23 +27,22 @@ import org.killbill.billing.callcontext.InternalTenantContext;
 import org.killbill.billing.catalog.api.Currency;
 import org.killbill.billing.invoice.api.InvoicePayment;
 import org.killbill.billing.util.entity.dao.EntitySqlDao;
+import org.killbill.commons.jdbi.binder.SmartBindBean;
 import org.killbill.commons.jdbi.template.KillBillSqlDaoStringTemplate;
 import org.skife.jdbi.v2.sqlobject.Bind;
-import org.killbill.commons.jdbi.binder.SmartBindBean;
 import org.skife.jdbi.v2.sqlobject.SqlQuery;
 import org.skife.jdbi.v2.sqlobject.SqlUpdate;
 
 @KillBillSqlDaoStringTemplate
 public interface InvoicePaymentSqlDao extends EntitySqlDao<InvoicePaymentModelDao, InvoicePayment> {
 
-
     @SqlQuery
     public List<InvoicePaymentModelDao> getByPaymentId(@Bind("paymentId") final String paymentId,
                                                        @SmartBindBean final InternalTenantContext context);
 
     @SqlQuery
     public List<InvoicePaymentModelDao> getAllPaymentsForInvoiceIncludedInit(@Bind("invoiceId") final String invoiceId,
-                                                              @SmartBindBean final InternalTenantContext context);
+                                                                             @SmartBindBean final InternalTenantContext context);
 
     @SqlQuery
     List<InvoicePaymentModelDao> getInvoicePayments(@Bind("paymentId") final String paymentId,
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/glue/DefaultInvoiceModule.java b/invoice/src/main/java/org/killbill/billing/invoice/glue/DefaultInvoiceModule.java
index eabea2e..c2832cf 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/glue/DefaultInvoiceModule.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/glue/DefaultInvoiceModule.java
@@ -1,7 +1,7 @@
 /*
  * Copyright 2010-2013 Ning, Inc.
- * Copyright 2014-2015 Groupon, Inc
- * Copyright 2014-2015 The Billing Project, LLC
+ * Copyright 2014-2018 Groupon, Inc
+ * Copyright 2014-2018 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
@@ -27,12 +27,10 @@ import org.killbill.billing.invoice.api.DefaultInvoiceService;
 import org.killbill.billing.invoice.api.InvoiceApiHelper;
 import org.killbill.billing.invoice.api.InvoiceInternalApi;
 import org.killbill.billing.invoice.api.InvoiceListenerService;
-import org.killbill.billing.invoice.api.InvoicePaymentApi;
 import org.killbill.billing.invoice.api.InvoiceService;
 import org.killbill.billing.invoice.api.InvoiceUserApi;
 import org.killbill.billing.invoice.api.formatters.InvoiceFormatterFactory;
 import org.killbill.billing.invoice.api.formatters.ResourceBundleFactory;
-import org.killbill.billing.invoice.api.invoice.DefaultInvoicePaymentApi;
 import org.killbill.billing.invoice.api.svcs.DefaultInvoiceInternalApi;
 import org.killbill.billing.invoice.api.user.DefaultInvoiceUserApi;
 import org.killbill.billing.invoice.config.MultiTenantInvoiceConfig;
@@ -85,11 +83,6 @@ public class DefaultInvoiceModule extends KillBillModule implements InvoiceModul
         bind(InvoiceInternalApi.class).to(DefaultInvoiceInternalApi.class).asEagerSingleton();
     }
 
-    @Override
-    public void installInvoicePaymentApi() {
-        bind(InvoicePaymentApi.class).to(DefaultInvoicePaymentApi.class).asEagerSingleton();
-    }
-
     protected void installConfig() {
         installConfig(new ConfigurationObjectFactory(skifeConfigSource).build(InvoiceConfig.class));
     }
@@ -154,7 +147,6 @@ public class DefaultInvoiceModule extends KillBillModule implements InvoiceModul
         installInvoiceDao();
         installInvoiceUserApi();
         installInvoiceInternalApi();
-        installInvoicePaymentApi();
         installResourceBundleFactory();
         bind(RawUsageOptimizer.class).asEagerSingleton();
         bind(InvoiceApiHelper.class).asEagerSingleton();
diff --git a/invoice/src/main/resources/org/killbill/billing/invoice/dao/InvoicePaymentSqlDao.sql.stg b/invoice/src/main/resources/org/killbill/billing/invoice/dao/InvoicePaymentSqlDao.sql.stg
index 140ff05..3552f3c 100644
--- a/invoice/src/main/resources/org/killbill/billing/invoice/dao/InvoicePaymentSqlDao.sql.stg
+++ b/invoice/src/main/resources/org/killbill/billing/invoice/dao/InvoicePaymentSqlDao.sql.stg
@@ -72,8 +72,6 @@ getAllPaymentsForInvoiceIncludedInit() ::= <<
   ;
 >>
 
-
-
 getInvoicePayments() ::= <<
     SELECT <allTableFields("")>
     FROM <tableName()>
diff --git a/invoice/src/test/java/org/killbill/billing/invoice/dao/MockInvoiceDao.java b/invoice/src/test/java/org/killbill/billing/invoice/dao/MockInvoiceDao.java
index 4ce8e42..0935479 100644
--- a/invoice/src/test/java/org/killbill/billing/invoice/dao/MockInvoiceDao.java
+++ b/invoice/src/test/java/org/killbill/billing/invoice/dao/MockInvoiceDao.java
@@ -1,7 +1,7 @@
 /*
  * Copyright 2010-2013 Ning, Inc.
- * Copyright 2014-2016 Groupon, Inc
- * Copyright 2014-2016 The Billing Project, LLC
+ * Copyright 2014-2018 Groupon, Inc
+ * Copyright 2014-2018 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
@@ -245,6 +245,11 @@ public class MockInvoiceDao extends MockEntityDaoBase<InvoiceModelDao, Invoice, 
     }
 
     @Override
+    public InvoicePaymentModelDao getInvoicePaymentByCookieId(final String cookieId, final InternalTenantContext internalTenantContext) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
     public void notifyOfPaymentCompletion(final InvoicePaymentModelDao invoicePayment, final InternalCallContext context) {
         synchronized (monitor) {
             payments.put(invoicePayment.getId(), invoicePayment);
diff --git a/invoice/src/test/java/org/killbill/billing/invoice/InvoiceTestSuiteNoDB.java b/invoice/src/test/java/org/killbill/billing/invoice/InvoiceTestSuiteNoDB.java
index e097fac..6c064ab 100644
--- a/invoice/src/test/java/org/killbill/billing/invoice/InvoiceTestSuiteNoDB.java
+++ b/invoice/src/test/java/org/killbill/billing/invoice/InvoiceTestSuiteNoDB.java
@@ -22,7 +22,6 @@ import org.killbill.billing.GuicyKillbillTestSuiteNoDB;
 import org.killbill.billing.account.api.AccountInternalApi;
 import org.killbill.billing.currency.api.CurrencyConversionApi;
 import org.killbill.billing.invoice.api.InvoiceInternalApi;
-import org.killbill.billing.invoice.api.InvoicePaymentApi;
 import org.killbill.billing.invoice.api.InvoiceUserApi;
 import org.killbill.billing.invoice.api.formatters.ResourceBundleFactory;
 import org.killbill.billing.invoice.dao.InvoiceDao;
@@ -33,6 +32,7 @@ import org.killbill.billing.invoice.glue.TestInvoiceModuleNoDB;
 import org.killbill.billing.invoice.usage.RawUsageOptimizer;
 import org.killbill.billing.junction.BillingInternalApi;
 import org.killbill.billing.lifecycle.api.BusService;
+import org.killbill.billing.payment.api.InvoicePaymentApi;
 import org.killbill.billing.platform.api.KillbillConfigSource;
 import org.killbill.billing.subscription.api.SubscriptionBaseInternalApi;
 import org.killbill.billing.usage.api.UsageUserApi;
@@ -65,8 +65,6 @@ public abstract class InvoiceTestSuiteNoDB extends GuicyKillbillTestSuiteNoDB {
     @Inject
     protected InvoiceUserApi invoiceUserApi;
     @Inject
-    protected InvoicePaymentApi invoicePaymentApi;
-    @Inject
     protected InvoiceGenerator generator;
     @Inject
     protected InvoiceConfig invoiceConfig;
diff --git a/invoice/src/test/java/org/killbill/billing/invoice/InvoiceTestSuiteWithEmbeddedDB.java b/invoice/src/test/java/org/killbill/billing/invoice/InvoiceTestSuiteWithEmbeddedDB.java
index 523e480..1ecaa5f 100644
--- a/invoice/src/test/java/org/killbill/billing/invoice/InvoiceTestSuiteWithEmbeddedDB.java
+++ b/invoice/src/test/java/org/killbill/billing/invoice/InvoiceTestSuiteWithEmbeddedDB.java
@@ -24,7 +24,6 @@ import org.killbill.billing.account.api.AccountUserApi;
 import org.killbill.billing.catalog.api.Currency;
 import org.killbill.billing.invoice.api.DefaultInvoiceService;
 import org.killbill.billing.invoice.api.InvoiceInternalApi;
-import org.killbill.billing.invoice.api.InvoicePaymentApi;
 import org.killbill.billing.invoice.api.InvoiceService;
 import org.killbill.billing.invoice.api.InvoiceUserApi;
 import org.killbill.billing.invoice.dao.InvoiceDao;
@@ -33,6 +32,7 @@ import org.killbill.billing.invoice.glue.TestInvoiceModuleWithEmbeddedDb;
 import org.killbill.billing.invoice.notification.NextBillingDateNotifier;
 import org.killbill.billing.junction.BillingInternalApi;
 import org.killbill.billing.lifecycle.api.BusService;
+import org.killbill.billing.payment.api.InvoicePaymentApi;
 import org.killbill.billing.platform.api.KillbillConfigSource;
 import org.killbill.billing.subscription.api.SubscriptionBaseInternalApi;
 import org.killbill.billing.util.api.TagUserApi;
@@ -70,8 +70,6 @@ public abstract class InvoiceTestSuiteWithEmbeddedDB extends GuicyKillbillTestSu
     @Inject
     protected InvoiceUserApi invoiceUserApi;
     @Inject
-    protected InvoicePaymentApi invoicePaymentApi;
-    @Inject
     protected InvoiceGenerator generator;
     @Inject
     protected BillingInternalApi billingApi;
diff --git a/invoice/src/test/java/org/killbill/billing/invoice/TestInvoiceHelper.java b/invoice/src/test/java/org/killbill/billing/invoice/TestInvoiceHelper.java
index 30d07e7..431f709 100644
--- a/invoice/src/test/java/org/killbill/billing/invoice/TestInvoiceHelper.java
+++ b/invoice/src/test/java/org/killbill/billing/invoice/TestInvoiceHelper.java
@@ -38,6 +38,7 @@ import org.killbill.billing.account.api.AccountUserApi;
 import org.killbill.billing.account.api.ImmutableAccountInternalApi;
 import org.killbill.billing.callcontext.InternalCallContext;
 import org.killbill.billing.callcontext.InternalTenantContext;
+import org.killbill.billing.callcontext.MutableCallContext;
 import org.killbill.billing.callcontext.MutableInternalCallContext;
 import org.killbill.billing.catalog.MockPlan;
 import org.killbill.billing.catalog.MockPlanPhase;
@@ -264,16 +265,18 @@ public class TestInvoiceHelper {
                                                             .paymentMethodId(UUID.randomUUID())
                                                             .timeZone(DateTimeZone.UTC)
                                                             .createdDate(clock.getUTCNow())
+
                                                             .build();
+        final MutableCallContext mutableCallContext = new MutableCallContext(internalCallContext);
 
         final Account account;
         if (isFastTest()) {
-            account = GuicyKillbillTestSuiteNoDB.createMockAccount(accountData, accountUserApi, accountApi, immutableAccountApi, nonEntityDao, clock, internalCallContextFactory, callContext, internalCallContext);
+            account = GuicyKillbillTestSuiteNoDB.createMockAccount(accountData, accountUserApi, accountApi, immutableAccountApi, nonEntityDao, clock, internalCallContextFactory, mutableCallContext, internalCallContext);
         } else {
             account = accountUserApi.createAccount(accountData, callContext);
         }
 
-        GuicyKillbillTestSuite.refreshCallContext(account.getId(), clock, internalCallContextFactory, callContext, internalCallContext);
+        GuicyKillbillTestSuite.refreshCallContext(account.getId(), clock, internalCallContextFactory, mutableCallContext, internalCallContext);
 
         return account;
     }
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/AccountResource.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/AccountResource.java
index 5217c67..e1a9249 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/AccountResource.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/AccountResource.java
@@ -78,7 +78,6 @@ import org.killbill.billing.invoice.api.InvoiceApiException;
 import org.killbill.billing.invoice.api.InvoiceItem;
 import org.killbill.billing.invoice.api.InvoiceItemType;
 import org.killbill.billing.invoice.api.InvoicePayment;
-import org.killbill.billing.invoice.api.InvoicePaymentApi;
 import org.killbill.billing.invoice.api.InvoiceUserApi;
 import org.killbill.billing.jaxrs.JaxrsExecutors;
 import org.killbill.billing.jaxrs.json.AccountEmailJson;
@@ -102,6 +101,7 @@ import org.killbill.billing.overdue.api.OverdueApi;
 import org.killbill.billing.overdue.api.OverdueApiException;
 import org.killbill.billing.overdue.api.OverdueState;
 import org.killbill.billing.overdue.config.api.OverdueException;
+import org.killbill.billing.payment.api.InvoicePaymentApi;
 import org.killbill.billing.payment.api.Payment;
 import org.killbill.billing.payment.api.PaymentApi;
 import org.killbill.billing.payment.api.PaymentApiException;
@@ -188,7 +188,7 @@ public class AccountResource extends JaxRsResourceBase {
                            final Context context,
                            final RecordIdApi recordIdApi,
                            final NotificationQueueService notificationQueueService) {
-        super(uriBuilder, tagUserApi, customFieldUserApi, auditUserApi, accountApi, paymentApi, subscriptionApi, clock, context);
+        super(uriBuilder, tagUserApi, customFieldUserApi, auditUserApi, accountApi, paymentApi, invoicePaymentApi, subscriptionApi, clock, context);
         this.subscriptionApi = subscriptionApi;
         this.invoiceApi = invoiceApi;
         this.invoicePaymentApi = invoicePaymentApi;
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/AdminResource.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/AdminResource.java
index 415f8c7..454f7bf 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/AdminResource.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/AdminResource.java
@@ -17,7 +17,6 @@
 
 package org.killbill.billing.jaxrs.resources;
 
-import java.io.Closeable;
 import java.io.IOException;
 import java.io.OutputStream;
 import java.net.URI;
@@ -56,6 +55,7 @@ import org.killbill.billing.jaxrs.json.AdminPaymentJson;
 import org.killbill.billing.jaxrs.util.Context;
 import org.killbill.billing.jaxrs.util.JaxrsUriBuilder;
 import org.killbill.billing.payment.api.AdminPaymentApi;
+import org.killbill.billing.payment.api.InvoicePaymentApi;
 import org.killbill.billing.payment.api.Payment;
 import org.killbill.billing.payment.api.PaymentApi;
 import org.killbill.billing.payment.api.PaymentApiException;
@@ -127,6 +127,7 @@ public class AdminResource extends JaxRsResourceBase {
                          final AuditUserApi auditUserApi,
                          final AccountUserApi accountUserApi,
                          final PaymentApi paymentApi,
+                         final InvoicePaymentApi invoicePaymentApi,
                          final AdminPaymentApi adminPaymentApi,
                          final InvoiceUserApi invoiceUserApi,
                          final CacheControllerDispatcher cacheControllerDispatcher,
@@ -137,7 +138,7 @@ public class AdminResource extends JaxRsResourceBase {
                          final KillbillHealthcheck killbillHealthcheck,
                          final Clock clock,
                          final Context context) {
-        super(uriBuilder, tagUserApi, customFieldUserApi, auditUserApi, accountUserApi, paymentApi, null, clock, context);
+        super(uriBuilder, tagUserApi, customFieldUserApi, auditUserApi, accountUserApi, paymentApi, invoicePaymentApi, null, clock, context);
         this.adminPaymentApi = adminPaymentApi;
         this.invoiceUserApi = invoiceUserApi;
         this.tenantApi = tenantApi;
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/BundleResource.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/BundleResource.java
index 89af32f..8c2ea77 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/BundleResource.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/BundleResource.java
@@ -1,7 +1,9 @@
 /*
  * Copyright 2010-2013 Ning, Inc.
+ * Copyright 2014-2018 Groupon, Inc
+ * Copyright 2014-2018 The Billing Project, LLC
  *
- * Ning licenses this file to you under the Apache License, version 2.0
+ * 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:
  *
@@ -59,6 +61,7 @@ import org.killbill.billing.jaxrs.json.CustomFieldJson;
 import org.killbill.billing.jaxrs.json.TagJson;
 import org.killbill.billing.jaxrs.util.Context;
 import org.killbill.billing.jaxrs.util.JaxrsUriBuilder;
+import org.killbill.billing.payment.api.InvoicePaymentApi;
 import org.killbill.billing.payment.api.PaymentApi;
 import org.killbill.billing.payment.api.PluginProperty;
 import org.killbill.billing.util.api.AuditUserApi;
@@ -105,9 +108,10 @@ public class BundleResource extends JaxRsResourceBase {
                           final SubscriptionApi subscriptionApi,
                           final EntitlementApi entitlementApi,
                           final PaymentApi paymentApi,
+                          final InvoicePaymentApi invoicePaymentApi,
                           final Clock clock,
                           final Context context) {
-        super(uriBuilder, tagUserApi, customFieldUserApi, auditUserApi, accountUserApi, paymentApi, subscriptionApi, clock, context);
+        super(uriBuilder, tagUserApi, customFieldUserApi, auditUserApi, accountUserApi, paymentApi, invoicePaymentApi, subscriptionApi, clock, context);
         this.entitlementApi = entitlementApi;
         this.subscriptionApi = subscriptionApi;
     }
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/CatalogResource.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/CatalogResource.java
index adb0acb..356ea48 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/CatalogResource.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/CatalogResource.java
@@ -66,6 +66,7 @@ import org.killbill.billing.jaxrs.json.PlanDetailJson;
 import org.killbill.billing.jaxrs.json.SimplePlanJson;
 import org.killbill.billing.jaxrs.util.Context;
 import org.killbill.billing.jaxrs.util.JaxrsUriBuilder;
+import org.killbill.billing.payment.api.InvoicePaymentApi;
 import org.killbill.billing.payment.api.PaymentApi;
 import org.killbill.billing.tenant.api.TenantApiException;
 import org.killbill.billing.util.api.AuditUserApi;
@@ -102,11 +103,12 @@ public class CatalogResource extends JaxRsResourceBase {
                            final AuditUserApi auditUserApi,
                            final AccountUserApi accountUserApi,
                            final PaymentApi paymentApi,
+                           final InvoicePaymentApi invoicePaymentApi,
                            final CatalogUserApi catalogUserApi,
                            final SubscriptionApi subscriptionApi,
                            final Clock clock,
                            final Context context) {
-        super(uriBuilder, tagUserApi, customFieldUserApi, auditUserApi, accountUserApi, paymentApi, null, clock, context);
+        super(uriBuilder, tagUserApi, customFieldUserApi, auditUserApi, accountUserApi, paymentApi, invoicePaymentApi, null, clock, context);
         this.catalogUserApi = catalogUserApi;
         this.subscriptionApi = subscriptionApi;
     }
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/ComboPaymentResource.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/ComboPaymentResource.java
index f93fd74..655e3d0 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/ComboPaymentResource.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/ComboPaymentResource.java
@@ -1,6 +1,6 @@
 /*
- * Copyright 2015 Groupon, Inc
- * Copyright 2015 The Billing Project, LLC
+ * Copyright 2014-2018 Groupon, Inc
+ * Copyright 2014-2018 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
@@ -31,7 +31,7 @@ import org.killbill.billing.jaxrs.json.AccountJson;
 import org.killbill.billing.jaxrs.json.PaymentMethodJson;
 import org.killbill.billing.jaxrs.util.Context;
 import org.killbill.billing.jaxrs.util.JaxrsUriBuilder;
-import org.killbill.billing.payment.api.Payment;
+import org.killbill.billing.payment.api.InvoicePaymentApi;
 import org.killbill.billing.payment.api.PaymentApi;
 import org.killbill.billing.payment.api.PaymentApiException;
 import org.killbill.billing.payment.api.PaymentMethod;
@@ -40,10 +40,8 @@ import org.killbill.billing.util.api.AuditUserApi;
 import org.killbill.billing.util.api.CustomFieldUserApi;
 import org.killbill.billing.util.api.TagUserApi;
 import org.killbill.billing.util.callcontext.CallContext;
-import org.killbill.billing.util.callcontext.TenantContext;
 import org.killbill.clock.Clock;
 
-import com.google.common.base.Preconditions;
 import com.google.common.base.Predicate;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Iterables;
@@ -57,9 +55,10 @@ public abstract class ComboPaymentResource extends JaxRsResourceBase {
                                 final AuditUserApi auditUserApi,
                                 final AccountUserApi accountUserApi,
                                 final PaymentApi paymentApi,
+                                final InvoicePaymentApi invoicePaymentApi,
                                 final Clock clock,
                                 final Context context) {
-        super(uriBuilder, tagUserApi, customFieldUserApi, auditUserApi, accountUserApi, paymentApi, null, clock, context);
+        super(uriBuilder, tagUserApi, customFieldUserApi, auditUserApi, accountUserApi, paymentApi, invoicePaymentApi, null, clock, context);
     }
 
     protected Account getOrCreateAccount(final AccountJson accountJson, final CallContext callContext) throws AccountApiException {
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/CreditResource.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/CreditResource.java
index 8634bb4..f68cb30 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/CreditResource.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/CreditResource.java
@@ -46,6 +46,7 @@ import org.killbill.billing.invoice.api.InvoiceUserApi;
 import org.killbill.billing.jaxrs.json.CreditJson;
 import org.killbill.billing.jaxrs.util.Context;
 import org.killbill.billing.jaxrs.util.JaxrsUriBuilder;
+import org.killbill.billing.payment.api.InvoicePaymentApi;
 import org.killbill.billing.payment.api.PaymentApi;
 import org.killbill.billing.payment.api.PluginProperty;
 import org.killbill.billing.util.api.AuditUserApi;
@@ -80,9 +81,10 @@ public class CreditResource extends JaxRsResourceBase {
                           final CustomFieldUserApi customFieldUserApi,
                           final AuditUserApi auditUserApi,
                           final PaymentApi paymentApi,
+                          final InvoicePaymentApi invoicePaymentApi,
                           final Clock clock,
                           final Context context) {
-        super(uriBuilder, tagUserApi, customFieldUserApi, auditUserApi, accountUserApi, paymentApi, null, clock, context);
+        super(uriBuilder, tagUserApi, customFieldUserApi, auditUserApi, accountUserApi, paymentApi, invoicePaymentApi, null, clock, context);
         this.invoiceUserApi = invoiceUserApi;
         this.accountUserApi = accountUserApi;
     }
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/CustomFieldResource.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/CustomFieldResource.java
index 92a8f76..ad85ec7 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/CustomFieldResource.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/CustomFieldResource.java
@@ -1,7 +1,9 @@
 /*
  * Copyright 2010-2014 Ning, Inc.
+ * Copyright 2014-2018 Groupon, Inc
+ * Copyright 2014-2018 The Billing Project, LLC
  *
- * Ning licenses this file to you under the Apache License, version 2.0
+ * 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:
  *
@@ -37,6 +39,7 @@ import org.killbill.billing.jaxrs.json.AuditLogJson;
 import org.killbill.billing.jaxrs.json.CustomFieldJson;
 import org.killbill.billing.jaxrs.util.Context;
 import org.killbill.billing.jaxrs.util.JaxrsUriBuilder;
+import org.killbill.billing.payment.api.InvoicePaymentApi;
 import org.killbill.billing.payment.api.PaymentApi;
 import org.killbill.billing.util.api.AuditLevel;
 import org.killbill.billing.util.api.AuditUserApi;
@@ -74,9 +77,10 @@ public class CustomFieldResource extends JaxRsResourceBase {
                                final AuditUserApi auditUserApi,
                                final AccountUserApi accountUserApi,
                                final PaymentApi paymentApi,
+                               final InvoicePaymentApi invoicePaymentApi,
                                final Clock clock,
                                final Context context) {
-        super(uriBuilder, tagUserApi, customFieldUserApi, auditUserApi, accountUserApi, paymentApi, null, clock, context);
+        super(uriBuilder, tagUserApi, customFieldUserApi, auditUserApi, accountUserApi, paymentApi, invoicePaymentApi, null, clock, context);
     }
 
     @TimedResource
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/ExportResource.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/ExportResource.java
index e40ea0e..58220c3 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/ExportResource.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/ExportResource.java
@@ -1,7 +1,9 @@
 /*
  * Copyright 2010-2013 Ning, Inc.
+ * Copyright 2014-2018 Groupon, Inc
+ * Copyright 2014-2018 The Billing Project, LLC
  *
- * Ning licenses this file to you under the Apache License, version 2.0
+ * 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:
  *
@@ -34,6 +36,7 @@ import javax.ws.rs.core.StreamingOutput;
 import org.killbill.billing.account.api.AccountUserApi;
 import org.killbill.billing.jaxrs.util.Context;
 import org.killbill.billing.jaxrs.util.JaxrsUriBuilder;
+import org.killbill.billing.payment.api.InvoicePaymentApi;
 import org.killbill.billing.payment.api.PaymentApi;
 import org.killbill.billing.util.api.AuditUserApi;
 import org.killbill.billing.util.api.CustomFieldUserApi;
@@ -66,9 +69,10 @@ public class ExportResource extends JaxRsResourceBase {
                           final AuditUserApi auditUserApi,
                           final AccountUserApi accountUserApi,
                           final PaymentApi paymentApi,
+                          final InvoicePaymentApi invoicePaymentApi,
                           final Clock clock,
                           final Context context) {
-        super(uriBuilder, tagUserApi, customFieldUserApi, auditUserApi, accountUserApi, paymentApi, null, clock, context);
+        super(uriBuilder, tagUserApi, customFieldUserApi, auditUserApi, accountUserApi, paymentApi, invoicePaymentApi, null, clock, context);
         this.exportUserApi = exportUserApi;
     }
 
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/InvoiceItemResource.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/InvoiceItemResource.java
index 0c46744..2b8eb6b 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/InvoiceItemResource.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/InvoiceItemResource.java
@@ -45,6 +45,7 @@ import org.killbill.billing.jaxrs.json.CustomFieldJson;
 import org.killbill.billing.jaxrs.json.TagJson;
 import org.killbill.billing.jaxrs.util.Context;
 import org.killbill.billing.jaxrs.util.JaxrsUriBuilder;
+import org.killbill.billing.payment.api.InvoicePaymentApi;
 import org.killbill.billing.payment.api.PaymentApi;
 import org.killbill.billing.util.api.AuditUserApi;
 import org.killbill.billing.util.api.CustomFieldApiException;
@@ -73,8 +74,8 @@ public class InvoiceItemResource extends JaxRsResourceBase {
     @Inject
     public InvoiceItemResource(final JaxrsUriBuilder uriBuilder, final TagUserApi tagUserApi, final CustomFieldUserApi customFieldUserApi,
                                final AuditUserApi auditUserApi, final AccountUserApi accountUserApi, final PaymentApi paymentApi,
-                               final SubscriptionApi subscriptionApi, final Clock clock, final Context context) {
-        super(uriBuilder, tagUserApi, customFieldUserApi, auditUserApi, accountUserApi, paymentApi, subscriptionApi, clock, context);
+                               final InvoicePaymentApi invoicePaymentApi, final SubscriptionApi subscriptionApi, final Clock clock, final Context context) {
+        super(uriBuilder, tagUserApi, customFieldUserApi, auditUserApi, accountUserApi, paymentApi, invoicePaymentApi, subscriptionApi, clock, context);
     }
 
     @TimedResource
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/InvoicePaymentResource.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/InvoicePaymentResource.java
index 8632ec0..54aa634 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/InvoicePaymentResource.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/InvoicePaymentResource.java
@@ -1,7 +1,7 @@
 /*
  * Copyright 2010-2013 Ning, Inc.
- * Copyright 2014-2016 Groupon, Inc
- * Copyright 2014-2016 The Billing Project, LLC
+ * Copyright 2014-2018 Groupon, Inc
+ * Copyright 2014-2018 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
@@ -20,9 +20,7 @@ package org.killbill.billing.jaxrs.resources;
 
 import java.math.BigDecimal;
 import java.util.ArrayList;
-import java.util.Collection;
 import java.util.HashMap;
-import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
 import java.util.UUID;
@@ -47,8 +45,8 @@ import org.killbill.billing.ObjectType;
 import org.killbill.billing.account.api.Account;
 import org.killbill.billing.account.api.AccountApiException;
 import org.killbill.billing.account.api.AccountUserApi;
+import org.killbill.billing.catalog.api.Currency;
 import org.killbill.billing.invoice.api.InvoicePayment;
-import org.killbill.billing.invoice.api.InvoicePaymentApi;
 import org.killbill.billing.invoice.api.InvoicePaymentType;
 import org.killbill.billing.jaxrs.json.CustomFieldJson;
 import org.killbill.billing.jaxrs.json.InvoiceItemJson;
@@ -58,10 +56,14 @@ import org.killbill.billing.jaxrs.json.PaymentTransactionJson;
 import org.killbill.billing.jaxrs.json.TagJson;
 import org.killbill.billing.jaxrs.util.Context;
 import org.killbill.billing.jaxrs.util.JaxrsUriBuilder;
+import org.killbill.billing.payment.api.InvoicePaymentApi;
 import org.killbill.billing.payment.api.Payment;
 import org.killbill.billing.payment.api.PaymentApi;
 import org.killbill.billing.payment.api.PaymentApiException;
+import org.killbill.billing.payment.api.PaymentOptions;
+import org.killbill.billing.payment.api.PaymentTransaction;
 import org.killbill.billing.payment.api.PluginProperty;
+import org.killbill.billing.payment.api.TransactionStatus;
 import org.killbill.billing.util.UUIDs;
 import org.killbill.billing.util.api.AuditUserApi;
 import org.killbill.billing.util.api.CustomFieldApiException;
@@ -88,7 +90,7 @@ import io.swagger.annotations.ApiResponses;
 import static javax.ws.rs.core.MediaType.APPLICATION_JSON;
 
 @Path(JaxrsResource.INVOICE_PAYMENTS_PATH)
-@Api(value = JaxrsResource.INVOICE_PAYMENTS_PATH, description = "Operations on invoice payments", tags="InvoicePayment")
+@Api(value = JaxrsResource.INVOICE_PAYMENTS_PATH, description = "Operations on invoice payments", tags = "InvoicePayment")
 public class InvoicePaymentResource extends JaxRsResourceBase {
 
     private static final String ID_PARAM_NAME = "paymentId";
@@ -105,7 +107,7 @@ public class InvoicePaymentResource extends JaxRsResourceBase {
                                   final InvoicePaymentApi invoicePaymentApi,
                                   final Clock clock,
                                   final Context context) {
-        super(uriBuilder, tagUserApi, customFieldUserApi, auditUserApi, accountUserApi, paymentApi, null, clock, context);
+        super(uriBuilder, tagUserApi, customFieldUserApi, auditUserApi, accountUserApi, paymentApi, invoicePaymentApi, null, clock, context);
         this.invoicePaymentApi = invoicePaymentApi;
     }
 
@@ -169,37 +171,50 @@ public class InvoicePaymentResource extends JaxRsResourceBase {
         final Iterable<PluginProperty> pluginProperties;
         final String transactionExternalKey = json.getTransactionExternalKey() != null ? json.getTransactionExternalKey() : UUIDs.randomUUID().toString();
         final String paymentExternalKey = json.getPaymentExternalKey() != null ? json.getPaymentExternalKey() : UUIDs.randomUUID().toString();
-        if (json.isAdjusted() != null && json.isAdjusted()) {
-            if (json.getAdjustments() != null && json.getAdjustments().size() > 0) {
-                final Map<UUID, BigDecimal> adjustments = new HashMap<UUID, BigDecimal>();
+
+        final boolean isAdjusted = json.isAdjusted() != null && json.isAdjusted();
+        final Map<UUID, BigDecimal> adjustments = new HashMap<UUID, BigDecimal>();
+        if (isAdjusted) {
+            if (json.getAdjustments() != null && !json.getAdjustments().isEmpty()) {
                 for (final InvoiceItemJson item : json.getAdjustments()) {
                     adjustments.put(item.getInvoiceItemId(), item.getAmount());
                 }
-                pluginProperties = extractPluginProperties(pluginPropertiesString,
-                                                           new PluginProperty("IPCD_REFUND_WITH_ADJUSTMENTS", "true", false),
-                                                           new PluginProperty("IPCD_REFUND_IDS_AMOUNTS", adjustments, false));
+                pluginProperties = extractPluginProperties(pluginPropertiesString);
             } else {
-                pluginProperties = extractPluginProperties(pluginPropertiesString,
-                                                           new PluginProperty("IPCD_REFUND_WITH_ADJUSTMENTS", "true", false));
+                pluginProperties = extractPluginProperties(pluginPropertiesString);
             }
         } else {
             pluginProperties = extractPluginProperties(pluginPropertiesString);
         }
 
-        final Payment result;
+        final InvoicePayment result;
         if (externalPayment) {
-            UUID externalPaymentMethodId = paymentMethodId;
-
-            final Collection<PluginProperty> pluginPropertiesForExternalRefund = new LinkedList<PluginProperty>();
-            Iterables.addAll(pluginPropertiesForExternalRefund, pluginProperties);
-            pluginPropertiesForExternalRefund.add(new PluginProperty("IPCD_PAYMENT_ID", paymentId, false));
-
-            result = paymentApi.createCreditWithPaymentControl(account, externalPaymentMethodId, null, json.getAmount(), account.getCurrency(), json.getEffectiveDate(),
-                                                               paymentExternalKey, transactionExternalKey, pluginPropertiesForExternalRefund,
-                                                               createInvoicePaymentControlPluginApiPaymentOptions(true), callContext);
+            result = invoicePaymentApi.createCreditForInvoicePayment(isAdjusted,
+                                                                     adjustments,
+                                                                     account,
+                                                                     paymentId,
+                                                                     paymentMethodId,
+                                                                     null,
+                                                                     json.getAmount(),
+                                                                     account.getCurrency(),
+                                                                     json.getEffectiveDate(),
+                                                                     paymentExternalKey,
+                                                                     transactionExternalKey,
+                                                                     pluginProperties,
+                                                                     createInvoicePaymentControlPluginApiPaymentOptions(true),
+                                                                     callContext);
         } else {
-            result = paymentApi.createRefundWithPaymentControl(account, payment.getId(), json.getAmount(), account.getCurrency(), json.getEffectiveDate(), transactionExternalKey,
-                                                               pluginProperties, createInvoicePaymentControlPluginApiPaymentOptions(false), callContext);
+            result = invoicePaymentApi.createRefundForInvoicePayment(isAdjusted,
+                                                                     adjustments,
+                                                                     account,
+                                                                     payment.getId(),
+                                                                     json.getAmount(),
+                                                                     account.getCurrency(),
+                                                                     json.getEffectiveDate(),
+                                                                     transactionExternalKey,
+                                                                     pluginProperties,
+                                                                     createInvoicePaymentControlPluginApiPaymentOptions(false),
+                                                                     callContext);
         }
 
         return uriBuilder.buildResponse(uriInfo, InvoicePaymentResource.class, "getInvoicePayment", result.getId(), request);
@@ -230,7 +245,7 @@ public class InvoicePaymentResource extends JaxRsResourceBase {
         final String transactionExternalKey = json.getTransactionExternalKey() != null ? json.getTransactionExternalKey() : UUIDs.randomUUID().toString();
 
         final Payment result = paymentApi.createChargebackWithPaymentControl(account, payment.getId(), json.getAmount(), account.getCurrency(), json.getEffectiveDate(),
-                                                                                   transactionExternalKey, createInvoicePaymentControlPluginApiPaymentOptions(false), callContext);
+                                                                             transactionExternalKey, createInvoicePaymentControlPluginApiPaymentOptions(false), callContext);
         return uriBuilder.buildResponse(uriInfo, InvoicePaymentResource.class, "getInvoicePayment", result.getId(), request);
     }
 
@@ -261,7 +276,6 @@ public class InvoicePaymentResource extends JaxRsResourceBase {
         return uriBuilder.buildResponse(uriInfo, InvoicePaymentResource.class, "getInvoicePayment", result.getId(), request);
     }
 
-
     @TimedResource(name = "completeInvoicePaymentTransaction")
     @PUT
     @Path("/{paymentId:" + UUID_PATTERN + "}")
@@ -285,7 +299,6 @@ public class InvoicePaymentResource extends JaxRsResourceBase {
                                                       @HeaderParam(HDR_COMMENT) final String comment,
                                                       @javax.ws.rs.core.Context final UriInfo uriInfo,
                                                       @javax.ws.rs.core.Context final HttpServletRequest request) throws PaymentApiException, AccountApiException {
-
         final TenantContext tenantContext = context.createTenantContextNoAccountId(request);
         final Payment payment = paymentApi.getPayment(paymentId, false, false, ImmutableList.<PluginProperty>of(), tenantContext);
         final List<InvoicePayment> invoicePayments = invoicePaymentApi.getInvoicePayments(paymentId, tenantContext);
@@ -302,19 +315,34 @@ public class InvoicePaymentResource extends JaxRsResourceBase {
             return Response.status(Status.NOT_FOUND).build();
         }
 
-        final PluginProperty invoiceProperty = new PluginProperty("IPCD_INVOICE_ID" /* InvoicePaymentControlPluginApi.PROP_IPCD_INVOICE_ID (contract with plugin)  */,
-                                                                  invoiceId.toString(), false);
-        final Iterable<PluginProperty> pluginProperties = extractPluginProperties(pluginPropertiesString, invoiceProperty);
+        final Iterable<PluginProperty> pluginProperties = extractPluginProperties(pluginPropertiesString);
 
         final List<String> controlPluginNames = new ArrayList<String>();
-        controlPluginNames.add("__INVOICE_PAYMENT_CONTROL_PLUGIN__");
         controlPluginNames.addAll(paymentControlPluginNames);
 
-        completeTransactionInternal(json, payment, controlPluginNames, pluginProperties, tenantContext, createdBy, reason, comment, uriInfo, request);
+        final Account account = accountUserApi.getAccountById(payment.getAccountId(), tenantContext);
+        final BigDecimal amount = json == null ? null : json.getAmount();
+        final Currency currency = json == null ? null : json.getCurrency();
+
+        final CallContext callContext = context.createCallContextWithAccountId(account.getId(), createdBy, reason, comment, request);
+
+        final PaymentTransaction pendingOrSuccessTransaction = lookupPendingOrSuccessTransaction(payment,
+                                                                                                 json != null ? json.getTransactionId() : null,
+                                                                                                 json != null ? json.getTransactionExternalKey() : null,
+                                                                                                 json != null ? json.getTransactionType() : null);
+        // If transaction was already completed, return early (See #626)
+        if (pendingOrSuccessTransaction.getTransactionStatus() == TransactionStatus.SUCCESS) {
+            return Response.status(Status.NO_CONTENT).build();
+        }
+
+        final PaymentOptions paymentOptions = createControlPluginApiPaymentOptions(paymentControlPluginNames);
+        invoicePaymentApi.createPurchaseForInvoicePayment(account, invoiceId, payment.getPaymentMethodId(), payment.getId(), amount, currency, null,
+                                                          payment.getExternalKey(), pendingOrSuccessTransaction.getExternalKey(),
+                                                          pluginProperties, paymentOptions, callContext);
+
         return Response.status(Status.NO_CONTENT).build();
     }
 
-
     @TimedResource
     @GET
     @Path("/{paymentId:" + UUID_PATTERN + "}/" + CUSTOM_FIELDS)
@@ -346,7 +374,6 @@ public class InvoicePaymentResource extends JaxRsResourceBase {
                                         context.createCallContextNoAccountId(createdBy, reason, comment, request), uriInfo, request);
     }
 
-
     @TimedResource
     @PUT
     @Path("/{paymentId:" + UUID_PATTERN + "}/" + CUSTOM_FIELDS)
@@ -365,7 +392,6 @@ public class InvoicePaymentResource extends JaxRsResourceBase {
                                         context.createCallContextNoAccountId(createdBy, reason, comment, request));
     }
 
-
     @TimedResource
     @DELETE
     @Path("/{paymentId:" + UUID_PATTERN + "}/" + CUSTOM_FIELDS)
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/InvoiceResource.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/InvoiceResource.java
index ed67308..26d89a2 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/InvoiceResource.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/InvoiceResource.java
@@ -80,6 +80,7 @@ import org.killbill.billing.jaxrs.json.PhasePriceOverrideJson;
 import org.killbill.billing.jaxrs.json.TagJson;
 import org.killbill.billing.jaxrs.util.Context;
 import org.killbill.billing.jaxrs.util.JaxrsUriBuilder;
+import org.killbill.billing.payment.api.InvoicePaymentApi;
 import org.killbill.billing.payment.api.Payment;
 import org.killbill.billing.payment.api.PaymentApi;
 import org.killbill.billing.payment.api.PaymentApiException;
@@ -146,6 +147,7 @@ public class InvoiceResource extends JaxRsResourceBase {
     public InvoiceResource(final AccountUserApi accountUserApi,
                            final InvoiceUserApi invoiceApi,
                            final PaymentApi paymentApi,
+                           final InvoicePaymentApi invoicePaymentApi,
                            final Clock clock,
                            final JaxrsUriBuilder uriBuilder,
                            final TagUserApi tagUserApi,
@@ -153,7 +155,7 @@ public class InvoiceResource extends JaxRsResourceBase {
                            final AuditUserApi auditUserApi,
                            final TenantUserApi tenantApi,
                            final Context context) {
-        super(uriBuilder, tagUserApi, customFieldUserApi, auditUserApi, accountUserApi, paymentApi, null, clock, context);
+        super(uriBuilder, tagUserApi, customFieldUserApi, auditUserApi, accountUserApi, paymentApi, invoicePaymentApi, null, clock, context);
         this.invoiceApi = invoiceApi;
         this.tenantApi = tenantApi;
         this.defaultLocale = Locale.getDefault();
@@ -694,10 +696,10 @@ public class InvoiceResource extends JaxRsResourceBase {
         final UUID paymentMethodId = externalPayment ? null :
                                      (payment.getPaymentMethodId() != null ? payment.getPaymentMethodId() : account.getPaymentMethodId());
 
-        final Payment result = createPurchaseForInvoice(account, invoiceId, payment.getPurchasedAmount(), paymentMethodId, externalPayment,
-                                                        payment.getPaymentExternalKey(), null, pluginProperties, callContext);
+        final InvoicePayment result = createPurchaseForInvoice(account, invoiceId, payment.getPurchasedAmount(), paymentMethodId, externalPayment,
+                                                               payment.getPaymentExternalKey(), null, pluginProperties, callContext);
         return result != null ?
-               uriBuilder.buildResponse(uriInfo, InvoicePaymentResource.class, "getInvoicePayment", result.getId(), request) :
+               uriBuilder.buildResponse(uriInfo, InvoicePaymentResource.class, "getInvoicePayment", result.getPaymentId(), request) :
                Response.status(Status.NO_CONTENT).build();
     }
 
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/JaxRsResourceBase.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/JaxRsResourceBase.java
index 87b2c37..92dc6b6 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/JaxRsResourceBase.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/JaxRsResourceBase.java
@@ -22,7 +22,6 @@ import java.io.IOException;
 import java.io.OutputStream;
 import java.math.BigDecimal;
 import java.net.URI;
-import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
@@ -72,6 +71,7 @@ import org.killbill.billing.jaxrs.json.TagJson;
 import org.killbill.billing.jaxrs.util.Context;
 import org.killbill.billing.jaxrs.util.JaxrsUriBuilder;
 import org.killbill.billing.junction.DefaultBlockingState;
+import org.killbill.billing.payment.api.InvoicePaymentApi;
 import org.killbill.billing.payment.api.Payment;
 import org.killbill.billing.payment.api.PaymentApi;
 import org.killbill.billing.payment.api.PaymentApiException;
@@ -132,6 +132,7 @@ public abstract class JaxRsResourceBase implements JaxrsResource {
     protected final AuditUserApi auditUserApi;
     protected final AccountUserApi accountUserApi;
     protected final PaymentApi paymentApi;
+    protected final InvoicePaymentApi invoicePaymentApi;
     protected final SubscriptionApi subscriptionApi;
     protected final Context context;
     protected final Clock clock;
@@ -145,6 +146,7 @@ public abstract class JaxRsResourceBase implements JaxrsResource {
                              final AuditUserApi auditUserApi,
                              final AccountUserApi accountUserApi,
                              final PaymentApi paymentApi,
+                             final InvoicePaymentApi invoicePaymentApi,
                              final SubscriptionApi subscriptionApi,
                              final Clock clock,
                              final Context context) {
@@ -154,6 +156,7 @@ public abstract class JaxRsResourceBase implements JaxrsResource {
         this.auditUserApi = auditUserApi;
         this.accountUserApi = accountUserApi;
         this.paymentApi = paymentApi;
+        this.invoicePaymentApi = invoicePaymentApi;
         this.subscriptionApi = subscriptionApi;
         this.clock = clock;
         this.context = context;
@@ -541,22 +544,20 @@ public abstract class JaxRsResourceBase implements JaxrsResource {
         return properties;
     }
 
-    protected Payment createPurchaseForInvoice(final Account account, final UUID invoiceId, final BigDecimal amountToPay, final UUID paymentMethodId, final Boolean externalPayment, final String paymentExternalKey, final String transactionExternalKey, final Iterable<PluginProperty> pluginProperties, final CallContext callContext) throws PaymentApiException {
-
-        final List<PluginProperty> properties = new ArrayList<PluginProperty>();
-        final Iterator<PluginProperty> pluginPropertyIterator = pluginProperties.iterator();
-        while (pluginPropertyIterator.hasNext()) {
-            properties.add(pluginPropertyIterator.next());
-        }
-
-        final PluginProperty invoiceProperty = new PluginProperty("IPCD_INVOICE_ID" /* InvoicePaymentControlPluginApi.PROP_IPCD_INVOICE_ID (contract with plugin)  */,
-                                                                  invoiceId.toString(), false);
-        properties.add(invoiceProperty);
+    protected InvoicePayment createPurchaseForInvoice(final Account account, final UUID invoiceId, final BigDecimal amountToPay, final UUID paymentMethodId, final Boolean externalPayment, final String paymentExternalKey, final String transactionExternalKey, final Iterable<PluginProperty> pluginProperties, final CallContext callContext) throws PaymentApiException {
         try {
-            return paymentApi.createPurchaseWithPaymentControl(account, paymentMethodId, null, amountToPay, account.getCurrency(), null, paymentExternalKey, transactionExternalKey,
-                                                               properties, createInvoicePaymentControlPluginApiPaymentOptions(externalPayment), callContext);
+            return invoicePaymentApi.createPurchaseForInvoicePayment(account,
+                                                                     invoiceId,
+                                                                     paymentMethodId,
+                                                                     null, amountToPay,
+                                                                     account.getCurrency(),
+                                                                     null,
+                                                                     paymentExternalKey,
+                                                                     transactionExternalKey,
+                                                                     pluginProperties,
+                                                                     createInvoicePaymentControlPluginApiPaymentOptions(externalPayment),
+                                                                     callContext);
         } catch (final PaymentApiException e) {
-
             if (e.getCode() == ErrorCode.PAYMENT_PLUGIN_EXCEPTION.getCode() /* &&
                 e.getMessage().contains("Invalid amount") */) { /* Plugin received bad input */
                 throw e;
@@ -568,7 +569,7 @@ public abstract class JaxRsResourceBase implements JaxrsResource {
     }
 
     protected PaymentOptions createInvoicePaymentControlPluginApiPaymentOptions(final boolean isExternalPayment) {
-        return createControlPluginApiPaymentOptions(isExternalPayment, ImmutableList.<String>of("__INVOICE_PAYMENT_CONTROL_PLUGIN__"));
+        return createControlPluginApiPaymentOptions(isExternalPayment, ImmutableList.<String>of());
     }
 
     protected PaymentOptions createControlPluginApiPaymentOptions(@Nullable final List<String> paymentControlPluginNames) {
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/NodesInfoResource.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/NodesInfoResource.java
index c16907c..ca518a4 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/NodesInfoResource.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/NodesInfoResource.java
@@ -1,6 +1,6 @@
 /*
- * Copyright 2014-2015 Groupon, Inc
- * Copyright 2014-2015 The Billing Project, LLC
+ * Copyright 2014-2018 Groupon, Inc
+ * Copyright 2014-2018 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
@@ -47,6 +47,7 @@ import org.killbill.billing.jaxrs.util.Context;
 import org.killbill.billing.jaxrs.util.JaxrsUriBuilder;
 import org.killbill.billing.osgi.api.PluginInfo;
 import org.killbill.billing.osgi.api.PluginServiceInfo;
+import org.killbill.billing.payment.api.InvoicePaymentApi;
 import org.killbill.billing.payment.api.PaymentApi;
 import org.killbill.billing.util.api.AuditUserApi;
 import org.killbill.billing.util.api.CustomFieldUserApi;
@@ -84,10 +85,11 @@ public class NodesInfoResource extends JaxRsResourceBase {
                              final AuditUserApi auditUserApi,
                              final AccountUserApi accountUserApi,
                              final PaymentApi paymentApi,
+                             final InvoicePaymentApi invoicePaymentApi,
                              final KillbillNodesApi killbillInfoApi,
                              final Clock clock,
                              final Context context) {
-        super(uriBuilder, tagUserApi, customFieldUserApi, auditUserApi, accountUserApi, paymentApi, null, clock, context);
+        super(uriBuilder, tagUserApi, customFieldUserApi, auditUserApi, accountUserApi, paymentApi, invoicePaymentApi, null, clock, context);
         this.killbillInfoApi = killbillInfoApi;
     }
 
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/OverdueResource.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/OverdueResource.java
index 782c07a..6b6009a 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/OverdueResource.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/OverdueResource.java
@@ -1,6 +1,6 @@
 /*
- * Copyright 2014 Groupon, Inc
- * Copyright 2014 The Billing Project, LLC
+ * Copyright 2014-2018 Groupon, Inc
+ * Copyright 2014-2018 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
@@ -39,6 +39,7 @@ import org.killbill.billing.jaxrs.util.JaxrsUriBuilder;
 import org.killbill.billing.overdue.api.OverdueApi;
 import org.killbill.billing.overdue.api.OverdueConfig;
 import org.killbill.billing.overdue.config.DefaultOverdueConfig;
+import org.killbill.billing.payment.api.InvoicePaymentApi;
 import org.killbill.billing.payment.api.PaymentApi;
 import org.killbill.billing.util.api.AuditUserApi;
 import org.killbill.billing.util.api.CustomFieldUserApi;
@@ -74,10 +75,11 @@ public class OverdueResource extends JaxRsResourceBase {
                            final AuditUserApi auditUserApi,
                            final AccountUserApi accountUserApi,
                            final PaymentApi paymentApi,
+                           final InvoicePaymentApi invoicePaymentApi,
                            final OverdueApi overdueApi,
                            final Clock clock,
                            final Context context) {
-        super(uriBuilder, tagUserApi, customFieldUserApi, auditUserApi, accountUserApi, paymentApi, null, clock, context);
+        super(uriBuilder, tagUserApi, customFieldUserApi, auditUserApi, accountUserApi, paymentApi, invoicePaymentApi, null, clock, context);
         this.overdueApi = overdueApi;
     }
 
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/PaymentGatewayResource.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/PaymentGatewayResource.java
index 93dfef0..2d80b56 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/PaymentGatewayResource.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/PaymentGatewayResource.java
@@ -1,6 +1,6 @@
 /*
- * Copyright 2014-2015 Groupon, Inc
- * Copyright 2014-2015 The Billing Project, LLC
+ * Copyright 2014-2018 Groupon, Inc
+ * Copyright 2014-2018 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
@@ -41,6 +41,7 @@ import org.killbill.billing.jaxrs.json.HostedPaymentPageFieldsJson;
 import org.killbill.billing.jaxrs.json.HostedPaymentPageFormDescriptorJson;
 import org.killbill.billing.jaxrs.util.Context;
 import org.killbill.billing.jaxrs.util.JaxrsUriBuilder;
+import org.killbill.billing.payment.api.InvoicePaymentApi;
 import org.killbill.billing.payment.api.PaymentApi;
 import org.killbill.billing.payment.api.PaymentApiException;
 import org.killbill.billing.payment.api.PaymentGatewayApi;
@@ -80,9 +81,10 @@ public class PaymentGatewayResource extends ComboPaymentResource {
                                   final AccountUserApi accountUserApi,
                                   final PaymentGatewayApi paymentGatewayApi,
                                   final PaymentApi paymentApi,
+                                  final InvoicePaymentApi invoicePaymentApi,
                                   final Clock clock,
                                   final Context context) {
-        super(uriBuilder, tagUserApi, customFieldUserApi, auditUserApi, accountUserApi, paymentApi, clock, context);
+        super(uriBuilder, tagUserApi, customFieldUserApi, auditUserApi, accountUserApi, paymentApi, invoicePaymentApi, clock, context);
         this.paymentGatewayApi = paymentGatewayApi;
     }
 
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/PaymentMethodResource.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/PaymentMethodResource.java
index 84e76a5..04cd766 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/PaymentMethodResource.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/PaymentMethodResource.java
@@ -1,7 +1,7 @@
 /*
  * Copyright 2010-2013 Ning, Inc.
- * Copyright 2014 Groupon, Inc
- * Copyright 2014 The Billing Project, LLC
+ * Copyright 2014-2018 Groupon, Inc
+ * Copyright 2014-2018 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
@@ -50,6 +50,7 @@ import org.killbill.billing.jaxrs.json.CustomFieldJson;
 import org.killbill.billing.jaxrs.json.PaymentMethodJson;
 import org.killbill.billing.jaxrs.util.Context;
 import org.killbill.billing.jaxrs.util.JaxrsUriBuilder;
+import org.killbill.billing.payment.api.InvoicePaymentApi;
 import org.killbill.billing.payment.api.PaymentApi;
 import org.killbill.billing.payment.api.PaymentApiException;
 import org.killbill.billing.payment.api.PaymentMethod;
@@ -93,9 +94,10 @@ public class PaymentMethodResource extends JaxRsResourceBase {
                                  final CustomFieldUserApi customFieldUserApi,
                                  final AuditUserApi auditUserApi,
                                  final PaymentApi paymentApi,
+                                 final InvoicePaymentApi invoicePaymentApi,
                                  final Clock clock,
                                  final Context context) {
-        super(uriBuilder, tagUserApi, customFieldUserApi, auditUserApi, accountUserApi, paymentApi, null, clock, context);
+        super(uriBuilder, tagUserApi, customFieldUserApi, auditUserApi, accountUserApi, paymentApi, invoicePaymentApi, null, clock, context);
     }
 
     @TimedResource(name = "getPaymentMethod")
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/PaymentResource.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/PaymentResource.java
index 25d5865..27ccdf6 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/PaymentResource.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/PaymentResource.java
@@ -1,6 +1,6 @@
 /*
- * Copyright 2014-2016 Groupon, Inc
- * Copyright 2014-2016 The Billing Project, LLC
+ * Copyright 2014-2018 Groupon, Inc
+ * Copyright 2014-2018 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
@@ -55,6 +55,7 @@ import org.killbill.billing.jaxrs.json.PaymentTransactionJson;
 import org.killbill.billing.jaxrs.json.TagJson;
 import org.killbill.billing.jaxrs.util.Context;
 import org.killbill.billing.jaxrs.util.JaxrsUriBuilder;
+import org.killbill.billing.payment.api.InvoicePaymentApi;
 import org.killbill.billing.payment.api.Payment;
 import org.killbill.billing.payment.api.PaymentApi;
 import org.killbill.billing.payment.api.PaymentApiException;
@@ -104,9 +105,10 @@ public class PaymentResource extends ComboPaymentResource {
                            final AuditUserApi auditUserApi,
                            final AccountUserApi accountUserApi,
                            final PaymentApi paymentApi,
+                           final InvoicePaymentApi invoicePaymentApi,
                            final Clock clock,
                            final Context context) {
-        super(uriBuilder, tagUserApi, customFieldUserApi, auditUserApi, accountUserApi, paymentApi, clock, context);
+        super(uriBuilder, tagUserApi, customFieldUserApi, auditUserApi, accountUserApi, paymentApi, invoicePaymentApi, clock, context);
     }
 
     @TimedResource(name = "getPayment")
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/PluginInfoResource.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/PluginInfoResource.java
index 936ba41..9f0f41f 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/PluginInfoResource.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/PluginInfoResource.java
@@ -1,6 +1,6 @@
 /*
- * Copyright 2014-2015 Groupon, Inc
- * Copyright 2014-2015 The Billing Project, LLC
+ * Copyright 2014-2018 Groupon, Inc
+ * Copyright 2014-2018 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
@@ -32,6 +32,7 @@ import org.killbill.billing.jaxrs.util.Context;
 import org.killbill.billing.jaxrs.util.JaxrsUriBuilder;
 import org.killbill.billing.osgi.api.PluginInfo;
 import org.killbill.billing.osgi.api.PluginsInfoApi;
+import org.killbill.billing.payment.api.InvoicePaymentApi;
 import org.killbill.billing.payment.api.PaymentApi;
 import org.killbill.billing.util.api.AuditUserApi;
 import org.killbill.billing.util.api.CustomFieldUserApi;
@@ -60,10 +61,11 @@ public class PluginInfoResource extends JaxRsResourceBase {
                               final AuditUserApi auditUserApi,
                               final AccountUserApi accountUserApi,
                               final PaymentApi paymentApi,
+                              final InvoicePaymentApi invoicePaymentApi,
                               final PluginsInfoApi pluginsInfoApi,
                               final Clock clock,
                               final Context context) {
-        super(uriBuilder, tagUserApi, customFieldUserApi, auditUserApi, accountUserApi, paymentApi, null, clock, context);
+        super(uriBuilder, tagUserApi, customFieldUserApi, auditUserApi, accountUserApi, paymentApi, invoicePaymentApi, null, clock, context);
         this.pluginsInfoApi = pluginsInfoApi;
     }
 
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/PluginResource.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/PluginResource.java
index 5a14c1e..a9a2d4c 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/PluginResource.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/PluginResource.java
@@ -58,6 +58,7 @@ import javax.ws.rs.core.UriInfo;
 import org.killbill.billing.account.api.AccountUserApi;
 import org.killbill.billing.jaxrs.util.Context;
 import org.killbill.billing.jaxrs.util.JaxrsUriBuilder;
+import org.killbill.billing.payment.api.InvoicePaymentApi;
 import org.killbill.billing.payment.api.PaymentApi;
 import org.killbill.billing.util.api.AuditUserApi;
 import org.killbill.billing.util.api.CustomFieldUserApi;
@@ -94,9 +95,10 @@ public class PluginResource extends JaxRsResourceBase {
                           final AuditUserApi auditUserApi,
                           final AccountUserApi accountUserApi,
                           final PaymentApi paymentApi,
+                          final InvoicePaymentApi invoicePaymentApi,
                           final Clock clock,
                           final Context context) {
-        super(uriBuilder, tagUserApi, customFieldUserApi, auditUserApi, accountUserApi, paymentApi, null, clock, context);
+        super(uriBuilder, tagUserApi, customFieldUserApi, auditUserApi, accountUserApi, paymentApi, invoicePaymentApi, null, clock, context);
         this.osgiServlet = osgiServlet;
     }
 
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/SecurityResource.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/SecurityResource.java
index 1ccf9ab..dc77b6c 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/SecurityResource.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/SecurityResource.java
@@ -1,7 +1,9 @@
 /*
  * Copyright 2010-2013 Ning, Inc.
+ * Copyright 2014-2018 Groupon, Inc
+ * Copyright 2014-2018 The Billing Project, LLC
  *
- * Ning licenses this file to you under the Apache License, version 2.0
+ * 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:
  *
@@ -42,6 +44,7 @@ import org.killbill.billing.jaxrs.json.SubjectJson;
 import org.killbill.billing.jaxrs.json.UserRolesJson;
 import org.killbill.billing.jaxrs.util.Context;
 import org.killbill.billing.jaxrs.util.JaxrsUriBuilder;
+import org.killbill.billing.payment.api.InvoicePaymentApi;
 import org.killbill.billing.payment.api.PaymentApi;
 import org.killbill.billing.security.Permission;
 import org.killbill.billing.security.SecurityApiException;
@@ -79,9 +82,10 @@ public class SecurityResource extends JaxRsResourceBase {
                             final AuditUserApi auditUserApi,
                             final AccountUserApi accountUserApi,
                             final PaymentApi paymentApi,
+                            final InvoicePaymentApi invoicePaymentApi,
                             final Clock clock,
                             final Context context) {
-        super(uriBuilder, tagUserApi, customFieldUserApi, auditUserApi, accountUserApi, paymentApi, null, clock, context);
+        super(uriBuilder, tagUserApi, customFieldUserApi, auditUserApi, accountUserApi, paymentApi, invoicePaymentApi, null, clock, context);
         this.securityApi = securityApi;
     }
 
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/SubscriptionResource.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/SubscriptionResource.java
index 1d50fdb..409c855 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/SubscriptionResource.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/SubscriptionResource.java
@@ -86,6 +86,7 @@ import org.killbill.billing.jaxrs.json.TagJson;
 import org.killbill.billing.jaxrs.util.Context;
 import org.killbill.billing.jaxrs.util.JaxrsUriBuilder;
 import org.killbill.billing.jaxrs.util.KillbillEventHandler;
+import org.killbill.billing.payment.api.InvoicePaymentApi;
 import org.killbill.billing.payment.api.PaymentApi;
 import org.killbill.billing.payment.api.PluginProperty;
 import org.killbill.billing.util.api.AuditUserApi;
@@ -136,9 +137,10 @@ public class SubscriptionResource extends JaxRsResourceBase {
                                 final SubscriptionApi subscriptionApi,
                                 final AccountUserApi accountUserApi,
                                 final PaymentApi paymentApi,
+                                final InvoicePaymentApi invoicePaymentApi,
                                 final Clock clock,
                                 final Context context) {
-        super(uriBuilder, tagUserApi, customFieldUserApi, auditUserApi, accountUserApi, paymentApi, subscriptionApi, clock, context);
+        super(uriBuilder, tagUserApi, customFieldUserApi, auditUserApi, accountUserApi, paymentApi, invoicePaymentApi, subscriptionApi, clock, context);
         this.killbillHandler = killbillHandler;
         this.entitlementApi = entitlementApi;
         this.subscriptionApi = subscriptionApi;
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/TagDefinitionResource.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/TagDefinitionResource.java
index d72811b..ff8b124 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/TagDefinitionResource.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/TagDefinitionResource.java
@@ -1,7 +1,9 @@
 /*
  * Copyright 2010-2013 Ning, Inc.
+ * Copyright 2014-2018 Groupon, Inc
+ * Copyright 2014-2018 The Billing Project, LLC
  *
- * Ning licenses this file to you under the Apache License, version 2.0
+ * 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:
  *
@@ -43,6 +45,7 @@ import org.killbill.billing.jaxrs.json.AuditLogJson;
 import org.killbill.billing.jaxrs.json.TagDefinitionJson;
 import org.killbill.billing.jaxrs.util.Context;
 import org.killbill.billing.jaxrs.util.JaxrsUriBuilder;
+import org.killbill.billing.payment.api.InvoicePaymentApi;
 import org.killbill.billing.payment.api.PaymentApi;
 import org.killbill.billing.util.api.AuditLevel;
 import org.killbill.billing.util.api.AuditUserApi;
@@ -78,9 +81,10 @@ public class TagDefinitionResource extends JaxRsResourceBase {
                                  final AuditUserApi auditUserApi,
                                  final AccountUserApi accountUserApi,
                                  final PaymentApi paymentApi,
+                                 final InvoicePaymentApi invoicePaymentApi,
                                  final Clock clock,
                                  final Context context) {
-        super(uriBuilder, tagUserApi, customFieldUserApi, auditUserApi, accountUserApi, paymentApi, null, clock, context);
+        super(uriBuilder, tagUserApi, customFieldUserApi, auditUserApi, accountUserApi, paymentApi, invoicePaymentApi, null, clock, context);
     }
 
     @TimedResource
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/TagResource.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/TagResource.java
index 96114a0..4429014 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/TagResource.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/TagResource.java
@@ -1,7 +1,9 @@
 /*
  * Copyright 2010-2014 Ning, Inc.
+ * Copyright 2014-2018 Groupon, Inc
+ * Copyright 2014-2018 The Billing Project, LLC
  *
- * Ning licenses this file to you under the Apache License, version 2.0
+ * 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:
  *
@@ -36,6 +38,7 @@ import org.killbill.billing.ObjectType;
 import org.killbill.billing.account.api.AccountApiException;
 import org.killbill.billing.account.api.AccountUserApi;
 import org.killbill.billing.jaxrs.json.AuditLogJson;
+import org.killbill.billing.payment.api.InvoicePaymentApi;
 import org.killbill.billing.payment.api.PaymentApi;
 import org.killbill.billing.util.api.AuditLevel;
 import org.killbill.billing.util.audit.AuditLogWithHistory;
@@ -77,9 +80,10 @@ public class TagResource extends JaxRsResourceBase {
                        final AuditUserApi auditUserApi,
                        final AccountUserApi accountUserApi,
                        final PaymentApi paymentApi,
+                       final InvoicePaymentApi invoicePaymentApi,
                        final Clock clock,
                        final Context context) {
-        super(uriBuilder, tagUserApi, customFieldUserApi, auditUserApi, accountUserApi, paymentApi, null, clock, context);
+        super(uriBuilder, tagUserApi, customFieldUserApi, auditUserApi, accountUserApi, paymentApi, invoicePaymentApi, null, clock, context);
     }
 
     @TimedResource
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/TenantResource.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/TenantResource.java
index dabce06..ad072cf 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/TenantResource.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/TenantResource.java
@@ -1,7 +1,9 @@
 /*
  * Copyright 2010-2013 Ning, Inc.
+ * Copyright 2014-2018 Groupon, Inc
+ * Copyright 2014-2018 The Billing Project, LLC
  *
- * Ning licenses this file to you under the Apache License, version 2.0
+ * 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:
  *
@@ -46,6 +48,7 @@ import org.killbill.billing.jaxrs.json.TenantJson;
 import org.killbill.billing.jaxrs.json.TenantKeyValueJson;
 import org.killbill.billing.jaxrs.util.Context;
 import org.killbill.billing.jaxrs.util.JaxrsUriBuilder;
+import org.killbill.billing.payment.api.InvoicePaymentApi;
 import org.killbill.billing.payment.api.PaymentApi;
 import org.killbill.billing.tenant.api.Tenant;
 import org.killbill.billing.tenant.api.TenantApiException;
@@ -88,10 +91,11 @@ public class TenantResource extends JaxRsResourceBase {
                           final AuditUserApi auditUserApi,
                           final AccountUserApi accountUserApi,
                           final PaymentApi paymentApi,
+                          final InvoicePaymentApi invoicePaymentApi,
                           final CatalogUserApi catalogUserApi,
                           final Clock clock,
                           final Context context) {
-        super(uriBuilder, tagUserApi, customFieldUserApi, auditUserApi, accountUserApi, paymentApi, null, clock, context);
+        super(uriBuilder, tagUserApi, customFieldUserApi, auditUserApi, accountUserApi, paymentApi, invoicePaymentApi, null, clock, context);
         this.tenantApi = tenantApi;
         this.catalogUserApi = catalogUserApi;
     }
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/TestResource.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/TestResource.java
index 5a78b55..9e22594 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/TestResource.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/TestResource.java
@@ -23,11 +23,8 @@ import java.util.Iterator;
 import javax.inject.Inject;
 import javax.servlet.ServletRequest;
 import javax.servlet.http.HttpServletRequest;
-import javax.ws.rs.DELETE;
 import javax.ws.rs.DefaultValue;
 import javax.ws.rs.GET;
-import javax.ws.rs.HEAD;
-import javax.ws.rs.HeaderParam;
 import javax.ws.rs.POST;
 import javax.ws.rs.PUT;
 import javax.ws.rs.Path;
@@ -35,28 +32,23 @@ import javax.ws.rs.Produces;
 import javax.ws.rs.QueryParam;
 import javax.ws.rs.core.Response;
 import javax.ws.rs.core.Response.Status;
-import javax.ws.rs.core.UriInfo;
 
 import org.joda.time.DateTime;
 import org.joda.time.DateTimeZone;
 import org.joda.time.LocalDate;
 import org.killbill.billing.ObjectType;
 import org.killbill.billing.account.api.AccountUserApi;
-import org.killbill.billing.catalog.api.CatalogApiException;
 import org.killbill.billing.catalog.api.CatalogUserApi;
 import org.killbill.billing.jaxrs.util.Context;
 import org.killbill.billing.jaxrs.util.JaxrsUriBuilder;
+import org.killbill.billing.payment.api.InvoicePaymentApi;
 import org.killbill.billing.payment.api.PaymentApi;
-import org.killbill.billing.tenant.api.TenantApiException;
-import org.killbill.billing.tenant.api.TenantKV.TenantKey;
 import org.killbill.billing.tenant.api.TenantUserApi;
 import org.killbill.billing.util.api.AuditUserApi;
 import org.killbill.billing.util.api.CustomFieldUserApi;
 import org.killbill.billing.util.api.RecordIdApi;
 import org.killbill.billing.util.api.TagUserApi;
-import org.killbill.billing.util.cache.Cachable.CacheType;
 import org.killbill.billing.util.cache.CacheControllerDispatcher;
-import org.killbill.billing.util.callcontext.CallContext;
 import org.killbill.billing.util.callcontext.TenantContext;
 import org.killbill.bus.api.BusEvent;
 import org.killbill.bus.api.BusEventWithMetadata;
@@ -107,9 +99,9 @@ public class TestResource extends JaxRsResourceBase {
     public TestResource(final JaxrsUriBuilder uriBuilder, final TagUserApi tagUserApi, final CustomFieldUserApi customFieldUserApi,
                         final AuditUserApi auditUserApi, final AccountUserApi accountUserApi, final RecordIdApi recordIdApi,
                         final PersistentBus persistentBus, final NotificationQueueService notificationQueueService, final PaymentApi paymentApi,
-                        final TenantUserApi tenantApi, final CatalogUserApi catalogUserApi,
+                        final InvoicePaymentApi invoicePaymentApi, final TenantUserApi tenantApi, final CatalogUserApi catalogUserApi,
                         final Clock clock, final CacheControllerDispatcher cacheControllerDispatcher, final Context context) {
-        super(uriBuilder, tagUserApi, customFieldUserApi, auditUserApi, accountUserApi, paymentApi, null, clock, context);
+        super(uriBuilder, tagUserApi, customFieldUserApi, auditUserApi, accountUserApi, paymentApi, invoicePaymentApi, null, clock, context);
         this.persistentBus = persistentBus;
         this.notificationQueueService = notificationQueueService;
         this.recordIdApi = recordIdApi;
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/TransactionResource.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/TransactionResource.java
index cad1ac3..99bdd75 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/TransactionResource.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/TransactionResource.java
@@ -1,6 +1,6 @@
 /*
- * Copyright 2014 Groupon, Inc
- * Copyright 2014 The Billing Project, LLC
+ * Copyright 2014-2018 Groupon, Inc
+ * Copyright 2014-2018 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
@@ -48,6 +48,7 @@ import org.killbill.billing.jaxrs.json.PaymentTransactionJson;
 import org.killbill.billing.jaxrs.json.TagJson;
 import org.killbill.billing.jaxrs.util.Context;
 import org.killbill.billing.jaxrs.util.JaxrsUriBuilder;
+import org.killbill.billing.payment.api.InvoicePaymentApi;
 import org.killbill.billing.payment.api.Payment;
 import org.killbill.billing.payment.api.PaymentApi;
 import org.killbill.billing.payment.api.PaymentApiException;
@@ -90,9 +91,10 @@ public class TransactionResource extends JaxRsResourceBase {
                                final AuditUserApi auditUserApi,
                                final AccountUserApi accountUserApi,
                                final PaymentApi paymentApi,
+                               final InvoicePaymentApi invoicePaymentApi,
                                final Clock clock,
                                final Context context) {
-        super(uriBuilder, tagUserApi, customFieldUserApi, auditUserApi, accountUserApi, paymentApi, null, clock, context);
+        super(uriBuilder, tagUserApi, customFieldUserApi, auditUserApi, accountUserApi, paymentApi, invoicePaymentApi, null, clock, context);
     }
 
     @TimedResource(name = "getPaymentByTransactionId")
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/UsageResource.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/UsageResource.java
index 7aa2203..3660b35 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/UsageResource.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/UsageResource.java
@@ -1,5 +1,6 @@
 /*
- * Copyright 2014 The Billing Project, LLC
+ * Copyright 2014-2018 Groupon, Inc
+ * Copyright 2014-2018 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
@@ -37,7 +38,6 @@ import org.joda.time.LocalDate;
 import org.killbill.billing.account.api.AccountApiException;
 import org.killbill.billing.account.api.AccountUserApi;
 import org.killbill.billing.entitlement.api.Entitlement;
-import org.killbill.billing.entitlement.api.Entitlement.EntitlementState;
 import org.killbill.billing.entitlement.api.EntitlementApi;
 import org.killbill.billing.entitlement.api.EntitlementApiException;
 import org.killbill.billing.jaxrs.json.RolledUpUsageJson;
@@ -46,6 +46,7 @@ import org.killbill.billing.jaxrs.json.SubscriptionUsageRecordJson.UnitUsageReco
 import org.killbill.billing.jaxrs.json.SubscriptionUsageRecordJson.UsageRecordJson;
 import org.killbill.billing.jaxrs.util.Context;
 import org.killbill.billing.jaxrs.util.JaxrsUriBuilder;
+import org.killbill.billing.payment.api.InvoicePaymentApi;
 import org.killbill.billing.payment.api.PaymentApi;
 import org.killbill.billing.usage.api.RolledUpUsage;
 import org.killbill.billing.usage.api.SubscriptionUsageRecord;
@@ -89,10 +90,11 @@ public class UsageResource extends JaxRsResourceBase {
                          final AccountUserApi accountUserApi,
                          final UsageUserApi usageUserApi,
                          final PaymentApi paymentApi,
+                         final InvoicePaymentApi invoicePaymentApi,
                          final EntitlementApi entitlementApi,
                          final Clock clock,
                          final Context context) {
-        super(uriBuilder, tagUserApi, customFieldUserApi, auditUserApi, accountUserApi, paymentApi, null, clock, context);
+        super(uriBuilder, tagUserApi, customFieldUserApi, auditUserApi, accountUserApi, paymentApi, invoicePaymentApi, null, clock, context);
         this.usageUserApi = usageUserApi;
         this.entitlementApi = entitlementApi;
     }
diff --git a/jaxrs/src/test/java/org/killbill/billing/jaxrs/resources/TestJaxRsResourceBase.java b/jaxrs/src/test/java/org/killbill/billing/jaxrs/resources/TestJaxRsResourceBase.java
index 979f012..807b78f 100644
--- a/jaxrs/src/test/java/org/killbill/billing/jaxrs/resources/TestJaxRsResourceBase.java
+++ b/jaxrs/src/test/java/org/killbill/billing/jaxrs/resources/TestJaxRsResourceBase.java
@@ -1,6 +1,6 @@
 /*
- * Copyright 2015 Groupon, Inc
- * Copyright 2015 The Billing Project, LLC
+ * Copyright 2014-2018 Groupon, Inc
+ * Copyright 2014-2018 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
@@ -67,7 +67,7 @@ public class TestJaxRsResourceBase extends JaxrsTestSuiteNoDB {
         private static final class JaxRsResourceBaseTest extends JaxRsResourceBase {
 
         public JaxRsResourceBaseTest() {
-            super(null, null, null, null, null, null, null, null, null);
+            super(null, null, null, null, null, null, null, null, null, null);
         }
     }
 
@@ -113,7 +113,7 @@ public class TestJaxRsResourceBase extends JaxrsTestSuiteNoDB {
 
     private static class UsageResourceTest extends UsageResource {
         public UsageResourceTest() {
-            super(null, null, null, null, null, null, null, null, null, null);
+            super(null, null, null, null, null, null, null, null, null, null, null);
         }
     }
 }
diff --git a/jaxrs/src/test/java/org/killbill/billing/jaxrs/TestDateConversion.java b/jaxrs/src/test/java/org/killbill/billing/jaxrs/TestDateConversion.java
index f70b3e4..b7d5a78 100644
--- a/jaxrs/src/test/java/org/killbill/billing/jaxrs/TestDateConversion.java
+++ b/jaxrs/src/test/java/org/killbill/billing/jaxrs/TestDateConversion.java
@@ -1,7 +1,7 @@
 /*
  * Copyright 2010-2013 Ning, Inc.
- * Copyright 2014-2016 Groupon, Inc
- * Copyright 2014-2016 The Billing Project, LLC
+ * Copyright 2014-2018 Groupon, Inc
+ * Copyright 2014-2018 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
@@ -38,7 +38,7 @@ import org.killbill.billing.util.UUIDs;
 public class TestDateConversion extends JaxRsResourceBase {
 
     public TestDateConversion() throws AccountApiException {
-        super(null, null, null, null, Mockito.mock(AccountUserApi.class), null, null, new ClockMock(), null);
+        super(null, null, null, null, Mockito.mock(AccountUserApi.class), null, null, null, new ClockMock(), null);
     }
 
     public UUID setupAccount(DateTimeZone accountTimeZone) throws AccountApiException {
diff --git a/payment/src/main/java/org/killbill/billing/payment/api/DefaultApiBase.java b/payment/src/main/java/org/killbill/billing/payment/api/DefaultApiBase.java
index e2bcac5..20e99ad 100644
--- a/payment/src/main/java/org/killbill/billing/payment/api/DefaultApiBase.java
+++ b/payment/src/main/java/org/killbill/billing/payment/api/DefaultApiBase.java
@@ -25,6 +25,7 @@ import org.killbill.billing.callcontext.InternalTenantContext;
 import org.killbill.billing.payment.invoice.InvoicePaymentControlPluginApi;
 import org.killbill.billing.util.callcontext.CallContext;
 import org.killbill.billing.util.callcontext.InternalCallContextFactory;
+import org.killbill.billing.util.callcontext.TenantContext;
 import org.killbill.billing.util.config.definition.PaymentConfig;
 
 import com.google.common.collect.ImmutableList;
@@ -39,15 +40,14 @@ public class DefaultApiBase {
         this.internalCallContextFactory = internalCallContextFactory;
     }
 
-    protected List<String> toPaymentControlPluginNames(final PaymentOptions paymentOptions, final CallContext callContext) {
+    protected List<String> toPaymentControlPluginNames(final PaymentOptions paymentOptions, final TenantContext callContext) {
         final InternalTenantContext internalTenantContext = internalCallContextFactory.createInternalTenantContextWithoutAccountRecordId(callContext);
 
         // Special path for JAX-RS InvoicePayment endpoints (see JaxRsResourceBase)
         final List<String> controlPluginNames = paymentConfig.getPaymentControlPluginNames(internalTenantContext);
         if (controlPluginNames != null &&
             paymentOptions.getPaymentControlPluginNames() != null &&
-            paymentOptions.getPaymentControlPluginNames().size() == 1 &&
-            InvoicePaymentControlPluginApi.PLUGIN_NAME.equals(paymentOptions.getPaymentControlPluginNames().get(0))) {
+            paymentOptions.getPaymentControlPluginNames().isEmpty()) {
             final List<String> paymentControlPluginNames = new LinkedList<String>(paymentOptions.getPaymentControlPluginNames());
             paymentControlPluginNames.addAll(controlPluginNames);
             return paymentControlPluginNames;
diff --git a/payment/src/main/java/org/killbill/billing/payment/api/DefaultInvoicePaymentApi.java b/payment/src/main/java/org/killbill/billing/payment/api/DefaultInvoicePaymentApi.java
new file mode 100644
index 0000000..c836e97
--- /dev/null
+++ b/payment/src/main/java/org/killbill/billing/payment/api/DefaultInvoicePaymentApi.java
@@ -0,0 +1,177 @@
+/*
+ * Copyright 2014-2018 Groupon, Inc
+ * Copyright 2014-2018 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.Collection;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.UUID;
+
+import javax.annotation.Nullable;
+
+import org.joda.time.DateTime;
+import org.killbill.billing.account.api.Account;
+import org.killbill.billing.catalog.api.Currency;
+import org.killbill.billing.invoice.api.InvoiceInternalApi;
+import org.killbill.billing.invoice.api.InvoicePayment;
+import org.killbill.billing.payment.api.svcs.InvoicePaymentPaymentOptions;
+import org.killbill.billing.util.UUIDs;
+import org.killbill.billing.util.callcontext.CallContext;
+import org.killbill.billing.util.callcontext.InternalCallContextFactory;
+import org.killbill.billing.util.callcontext.TenantContext;
+
+import com.google.common.base.MoreObjects;
+import com.google.inject.Inject;
+
+public class DefaultInvoicePaymentApi implements InvoicePaymentApi {
+
+    private final PaymentApi paymentApi;
+    private final InvoiceInternalApi invoiceInternalApi;
+    private final InvoicePaymentInternalApi invoicePaymentInternalApi;
+    private final InternalCallContextFactory internalCallContextFactory;
+
+    @Inject
+    public DefaultInvoicePaymentApi(final PaymentApi paymentApi,
+                                    final InvoiceInternalApi invoiceInternalApi,
+                                    final InvoicePaymentInternalApi invoicePaymentInternalApi,
+                                    final InternalCallContextFactory internalCallContextFactory) {
+        this.paymentApi = paymentApi;
+        this.invoiceInternalApi = invoiceInternalApi;
+        this.invoicePaymentInternalApi = invoicePaymentInternalApi;
+        this.internalCallContextFactory = internalCallContextFactory;
+    }
+
+    @Override
+    public InvoicePayment createPurchaseForInvoicePayment(final Account account,
+                                                          final UUID invoiceId,
+                                                          final UUID paymentMethodId,
+                                                          final UUID paymentId,
+                                                          final BigDecimal amount,
+                                                          final Currency currency,
+                                                          final DateTime effectiveDate,
+                                                          final String paymentExternalKey,
+                                                          final String paymentTransactionExternalKey,
+                                                          final Iterable<PluginProperty> properties,
+                                                          final PaymentOptions paymentOptions,
+                                                          final CallContext context) throws PaymentApiException {
+        return invoicePaymentInternalApi.createPurchaseForInvoicePayment(true,
+                                                                         account,
+                                                                         invoiceId,
+                                                                         paymentMethodId,
+                                                                         paymentId,
+                                                                         amount,
+                                                                         currency,
+                                                                         effectiveDate,
+                                                                         paymentExternalKey,
+                                                                         paymentTransactionExternalKey,
+                                                                         properties,
+                                                                         paymentOptions,
+                                                                         internalCallContextFactory.createInternalCallContext(account.getId(), context));
+    }
+
+    @Override
+    public InvoicePayment createRefundForInvoicePayment(final boolean isAdjusted,
+                                                        final Map<UUID, BigDecimal> adjustments,
+                                                        final Account account,
+                                                        final UUID paymentId,
+                                                        final BigDecimal amount,
+                                                        final Currency currency,
+                                                        final DateTime effectiveDate,
+                                                        final String originalPaymentTransactionExternalKey,
+                                                        final Iterable<PluginProperty> originalProperties,
+                                                        final PaymentOptions paymentOptions,
+                                                        final CallContext context) throws PaymentApiException {
+        final Collection<PluginProperty> pluginProperties = preparePluginPropertiesForRefundOrCredit(isAdjusted, adjustments, originalProperties);
+        final String paymentTransactionExternalKey = MoreObjects.firstNonNull(originalPaymentTransactionExternalKey, UUIDs.randomUUID().toString());
+
+        paymentApi.createRefundWithPaymentControl(account,
+                                                  paymentId,
+                                                  amount,
+                                                  currency,
+                                                  effectiveDate,
+                                                  paymentTransactionExternalKey,
+                                                  pluginProperties,
+                                                  InvoicePaymentPaymentOptions.create(paymentOptions),
+                                                  context);
+
+        return invoiceInternalApi.getInvoicePaymentByCookieId(paymentTransactionExternalKey, context);
+    }
+
+    @Override
+    public InvoicePayment createCreditForInvoicePayment(final boolean isAdjusted,
+                                                        final Map<UUID, BigDecimal> adjustments,
+                                                        final Account account,
+                                                        final UUID originalPaymentId,
+                                                        final UUID paymentMethodId,
+                                                        final UUID paymentId,
+                                                        final BigDecimal amount,
+                                                        final Currency currency,
+                                                        final DateTime effectiveDate,
+                                                        final String paymentExternalKey,
+                                                        final String originalPaymentTransactionExternalKey,
+                                                        final Iterable<PluginProperty> originalProperties,
+                                                        final PaymentOptions paymentOptions,
+                                                        final CallContext context) throws PaymentApiException {
+        final Collection<PluginProperty> pluginProperties = preparePluginPropertiesForRefundOrCredit(isAdjusted, adjustments, originalProperties);
+        pluginProperties.add(new PluginProperty("IPCD_PAYMENT_ID", originalPaymentId, false));
+
+        final String paymentTransactionExternalKey = MoreObjects.firstNonNull(originalPaymentTransactionExternalKey, UUIDs.randomUUID().toString());
+
+        paymentApi.createCreditWithPaymentControl(account,
+                                                  paymentMethodId,
+                                                  paymentId,
+                                                  amount,
+                                                  currency,
+                                                  effectiveDate,
+                                                  paymentExternalKey,
+                                                  paymentTransactionExternalKey,
+                                                  pluginProperties,
+                                                  InvoicePaymentPaymentOptions.create(paymentOptions),
+                                                  context);
+
+        return invoiceInternalApi.getInvoicePaymentByCookieId(paymentTransactionExternalKey, context);
+    }
+
+    @Override
+    public List<InvoicePayment> getInvoicePayments(final UUID paymentId, final TenantContext context) {
+        return invoiceInternalApi.getInvoicePayments(paymentId, context);
+    }
+
+    @Override
+    public List<InvoicePayment> getInvoicePaymentsByAccount(final UUID accountId, final TenantContext context) {
+        return invoiceInternalApi.getInvoicePaymentsByAccount(accountId, context);
+    }
+
+    private Collection<PluginProperty> preparePluginPropertiesForRefundOrCredit(final boolean isAdjusted,
+                                                                                @Nullable final Map<UUID, BigDecimal> adjustments,
+                                                                                final Iterable<PluginProperty> originalProperties) {
+        final Collection<PluginProperty> pluginProperties = new LinkedList<PluginProperty>();
+        if (originalProperties != null) {
+            for (final PluginProperty pluginProperty : originalProperties) {
+                pluginProperties.add(pluginProperty);
+            }
+        }
+        pluginProperties.add(new PluginProperty("IPCD_REFUND_WITH_ADJUSTMENTS", isAdjusted, false));
+        if (adjustments != null) {
+            pluginProperties.add(new PluginProperty("IPCD_REFUND_IDS_AMOUNTS", adjustments, false));
+        }
+        return pluginProperties;
+    }
+}
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 c9ffb84..cf9d900 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
@@ -131,10 +131,6 @@ public class DefaultPaymentApi extends DefaultApiBase implements PaymentApi {
         }
 
         checkNotNullParameter(account, "account");
-        if (paymentId == null) {
-            checkNotNullParameter(amount, "amount");
-            checkNotNullParameter(currency, "currency");
-        }
         checkNotNullParameter(properties, "plugin properties");
         checkExternalKeyLength(paymentExternalKey);
 
@@ -228,8 +224,6 @@ public class DefaultPaymentApi extends DefaultApiBase implements PaymentApi {
 
         checkNotNullParameter(account, "account");
         checkNotNullParameter(paymentId, "paymentId");
-        checkNotNullParameter(amount, "amount");
-        checkNotNullParameter(currency, "currency");
         checkNotNullParameter(properties, "plugin properties");
         checkExternalKeyLength(paymentTransactionExternalKey);
 
@@ -323,11 +317,6 @@ public class DefaultPaymentApi extends DefaultApiBase implements PaymentApi {
         }
 
         checkNotNullParameter(account, "account");
-        if (paymentId == null) {
-            checkNotNullParameter(amount, "amount");
-            checkNotNullParameter(currency, "currency");
-        }
-
         checkNotNullParameter(properties, "plugin properties");
         checkExternalKeyLength(paymentTransactionExternalKey);
 
@@ -515,9 +504,6 @@ public class DefaultPaymentApi extends DefaultApiBase implements PaymentApi {
         }
 
         checkNotNullParameter(account, "account");
-        if (paymentId == null) {
-            checkNotNullParameter(currency, "currency");
-        }
         checkNotNullParameter(paymentId, "paymentId");
         checkNotNullParameter(properties, "plugin properties");
         checkExternalKeyLength(paymentTransactionExternalKey);
@@ -617,10 +603,6 @@ public class DefaultPaymentApi extends DefaultApiBase implements PaymentApi {
         }
 
         checkNotNullParameter(account, "account");
-        if (paymentId == null) {
-            checkNotNullParameter(amount, "amount");
-            checkNotNullParameter(currency, "currency");
-        }
         checkNotNullParameter(properties, "plugin properties");
         checkExternalKeyLength(paymentTransactionExternalKey);
 
@@ -816,8 +798,6 @@ public class DefaultPaymentApi extends DefaultApiBase implements PaymentApi {
         }
 
         checkNotNullParameter(account, "account");
-        checkNotNullParameter(amount, "amount");
-        checkNotNullParameter(currency, "currency");
         checkNotNullParameter(paymentId, "paymentId");
 
         final String transactionType = TransactionType.CHARGEBACK.name();
diff --git a/payment/src/main/java/org/killbill/billing/payment/api/svcs/DefaultInvoicePaymentInternalApi.java b/payment/src/main/java/org/killbill/billing/payment/api/svcs/DefaultInvoicePaymentInternalApi.java
new file mode 100644
index 0000000..9f99f7b
--- /dev/null
+++ b/payment/src/main/java/org/killbill/billing/payment/api/svcs/DefaultInvoicePaymentInternalApi.java
@@ -0,0 +1,150 @@
+/*
+ * Copyright 2014-2018 Groupon, Inc
+ * Copyright 2014-2018 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.svcs;
+
+import java.math.BigDecimal;
+import java.util.Collection;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.UUID;
+
+import org.joda.time.DateTime;
+import org.killbill.billing.account.api.Account;
+import org.killbill.billing.callcontext.InternalCallContext;
+import org.killbill.billing.catalog.api.Currency;
+import org.killbill.billing.invoice.api.InvoiceInternalApi;
+import org.killbill.billing.invoice.api.InvoicePayment;
+import org.killbill.billing.payment.api.DefaultApiBase;
+import org.killbill.billing.payment.api.InvoicePaymentInternalApi;
+import org.killbill.billing.payment.api.Payment;
+import org.killbill.billing.payment.api.PaymentApiException;
+import org.killbill.billing.payment.api.PaymentOptions;
+import org.killbill.billing.payment.api.PaymentTransaction;
+import org.killbill.billing.payment.api.PluginProperty;
+import org.killbill.billing.payment.api.TransactionType;
+import org.killbill.billing.payment.core.PaymentMethodProcessor;
+import org.killbill.billing.payment.core.PluginControlPaymentProcessor;
+import org.killbill.billing.util.UUIDs;
+import org.killbill.billing.util.callcontext.CallContext;
+import org.killbill.billing.util.callcontext.InternalCallContextFactory;
+import org.killbill.billing.util.config.definition.PaymentConfig;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.inject.Inject;
+
+import static org.killbill.billing.payment.logging.PaymentLoggingHelper.logEnterAPICall;
+import static org.killbill.billing.payment.logging.PaymentLoggingHelper.logExitAPICall;
+
+public class DefaultInvoicePaymentInternalApi extends DefaultApiBase implements InvoicePaymentInternalApi {
+
+    private static final Logger log = LoggerFactory.getLogger(DefaultInvoicePaymentInternalApi.class);
+
+    private final InvoiceInternalApi invoiceInternalApi;
+    private final PluginControlPaymentProcessor pluginControlPaymentProcessor;
+    private final PaymentMethodProcessor paymentMethodProcessor;
+
+    @Inject
+    public DefaultInvoicePaymentInternalApi(final InvoiceInternalApi invoiceInternalApi,
+                                            final PluginControlPaymentProcessor pluginControlPaymentProcessor,
+                                            final PaymentMethodProcessor paymentMethodProcessor,
+                                            final PaymentConfig paymentConfig,
+                                            final InternalCallContextFactory internalCallContextFactory) {
+        super(paymentConfig, internalCallContextFactory);
+        this.invoiceInternalApi = invoiceInternalApi;
+        this.pluginControlPaymentProcessor = pluginControlPaymentProcessor;
+        this.paymentMethodProcessor = paymentMethodProcessor;
+    }
+
+    @Override
+    public InvoicePayment createPurchaseForInvoicePayment(final boolean isApiPayment,
+                                                          final Account account,
+                                                          final UUID invoiceId,
+                                                          final UUID paymentMethodId,
+                                                          final UUID paymentId,
+                                                          final BigDecimal amount,
+                                                          final Currency currency,
+                                                          final DateTime effectiveDate,
+                                                          final String paymentExternalKey,
+                                                          final String paymentTransactionExternalKey,
+                                                          final Iterable<PluginProperty> originalProperties,
+                                                          final PaymentOptions paymentOptions,
+                                                          final InternalCallContext internalCallContext) throws PaymentApiException {
+        checkExternalKeyLength(paymentTransactionExternalKey);
+
+        final Collection<PluginProperty> pluginProperties = new LinkedList<PluginProperty>();
+        if (originalProperties != null) {
+            for (final PluginProperty pluginProperty : originalProperties) {
+                pluginProperties.add(pluginProperty);
+            }
+        }
+        pluginProperties.add(new PluginProperty("IPCD_INVOICE_ID", invoiceId.toString(), false));
+
+        // TODO should we add paymentConfig.getPaymentControlPluginNames(internalTenantContext)?
+        final List<String> paymentControlPluginNames = InvoicePaymentPaymentOptions.create(paymentOptions).getPaymentControlPluginNames();
+
+        final CallContext callContext = internalCallContextFactory.createCallContext(internalCallContext);
+
+        final UUID resolvedPaymentMethodId = (paymentMethodId == null && paymentOptions.isExternalPayment()) ?
+                                             paymentMethodProcessor.createOrGetExternalPaymentMethod(UUIDs.randomUUID().toString(), account, pluginProperties, callContext, internalCallContext) :
+                                             paymentMethodId;
+
+        final String transactionType = TransactionType.PURCHASE.name();
+        Payment payment = null;
+        PaymentTransaction paymentTransaction = null;
+        PaymentApiException exception = null;
+        try {
+            logEnterAPICall(log, transactionType, account, paymentMethodId, paymentId, null, amount, currency, paymentExternalKey, paymentTransactionExternalKey, null, paymentControlPluginNames);
+
+            payment = pluginControlPaymentProcessor.createPurchase(isApiPayment,
+                                                                   account,
+                                                                   resolvedPaymentMethodId,
+                                                                   paymentId,
+                                                                   amount,
+                                                                   currency,
+                                                                   effectiveDate,
+                                                                   paymentExternalKey,
+                                                                   paymentTransactionExternalKey,
+                                                                   pluginProperties,
+                                                                   paymentControlPluginNames,
+                                                                   callContext,
+                                                                   internalCallContext);
+
+            paymentTransaction = payment.getTransactions().get(payment.getTransactions().size() - 1);
+        } catch (final PaymentApiException e) {
+            exception = e;
+            throw e;
+        } finally {
+            logExitAPICall(log,
+                           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,
+                           paymentControlPluginNames,
+                           exception);
+        }
+
+        return paymentTransaction != null ? invoiceInternalApi.getInvoicePaymentByCookieId(paymentTransactionExternalKey, callContext) : null;
+    }
+}
diff --git a/payment/src/main/java/org/killbill/billing/payment/api/svcs/InvoicePaymentPaymentOptions.java b/payment/src/main/java/org/killbill/billing/payment/api/svcs/InvoicePaymentPaymentOptions.java
new file mode 100644
index 0000000..f6e81c8
--- /dev/null
+++ b/payment/src/main/java/org/killbill/billing/payment/api/svcs/InvoicePaymentPaymentOptions.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2014-2018 Groupon, Inc
+ * Copyright 2014-2018 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.svcs;
+
+import java.util.LinkedList;
+import java.util.List;
+
+import org.killbill.billing.payment.api.PaymentOptions;
+import org.killbill.billing.payment.invoice.InvoicePaymentControlPluginApi;
+
+public class InvoicePaymentPaymentOptions implements PaymentOptions {
+
+    private final boolean isExternalPayment;
+    private final List<String> paymentControlPluginNames;
+
+    public InvoicePaymentPaymentOptions(final boolean isExternalPayment, final List<String> getPaymentControlPluginNames) {
+        this.isExternalPayment = isExternalPayment;
+        this.paymentControlPluginNames = getPaymentControlPluginNames;
+    }
+
+    public static InvoicePaymentPaymentOptions create(final PaymentOptions paymentOptions) {
+        final List<String> paymentControlPluginNames = new LinkedList<String>();
+        paymentControlPluginNames.addAll(paymentOptions.getPaymentControlPluginNames());
+        if (!paymentControlPluginNames.contains(InvoicePaymentControlPluginApi.PLUGIN_NAME)) {
+            paymentControlPluginNames.add(InvoicePaymentControlPluginApi.PLUGIN_NAME);
+        }
+
+        return new InvoicePaymentPaymentOptions(paymentOptions.isExternalPayment(), paymentControlPluginNames);
+    }
+
+    @Override
+    public boolean isExternalPayment() {
+        return isExternalPayment;
+    }
+
+    @Override
+    public List<String> getPaymentControlPluginNames() {
+        return paymentControlPluginNames;
+    }
+
+    @Override
+    public String toString() {
+        final StringBuilder sb = new StringBuilder("InvoicePaymentPaymentOptions{");
+        sb.append("isExternalPayment=").append(isExternalPayment);
+        sb.append(", paymentControlPluginNames=").append(paymentControlPluginNames);
+        sb.append('}');
+        return sb.toString();
+    }
+}
diff --git a/payment/src/main/java/org/killbill/billing/payment/bus/PaymentBusEventHandler.java b/payment/src/main/java/org/killbill/billing/payment/bus/PaymentBusEventHandler.java
index 99973ac..7255944 100644
--- a/payment/src/main/java/org/killbill/billing/payment/bus/PaymentBusEventHandler.java
+++ b/payment/src/main/java/org/killbill/billing/payment/bus/PaymentBusEventHandler.java
@@ -1,7 +1,7 @@
 /*
  * Copyright 2010-2013 Ning, Inc.
- * Copyright 2014-2016 Groupon, Inc
- * Copyright 2014-2016 The Billing Project, LLC
+ * Copyright 2014-2018 Groupon, Inc
+ * Copyright 2014-2018 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
@@ -19,8 +19,6 @@
 package org.killbill.billing.payment.bus;
 
 import java.math.BigDecimal;
-import java.util.ArrayList;
-import java.util.Collection;
 import java.util.LinkedList;
 import java.util.List;
 
@@ -31,16 +29,11 @@ import org.killbill.billing.account.api.AccountInternalApi;
 import org.killbill.billing.callcontext.InternalCallContext;
 import org.killbill.billing.events.InvoiceCreationInternalEvent;
 import org.killbill.billing.events.PaymentInternalEvent;
-import org.killbill.billing.payment.api.Payment;
+import org.killbill.billing.payment.api.InvoicePaymentInternalApi;
 import org.killbill.billing.payment.api.PaymentApiException;
-import org.killbill.billing.payment.api.PaymentTransaction;
+import org.killbill.billing.payment.api.PaymentOptions;
 import org.killbill.billing.payment.api.PluginProperty;
-import org.killbill.billing.payment.api.TransactionType;
-import org.killbill.billing.payment.core.PluginControlPaymentProcessor;
 import org.killbill.billing.payment.core.janitor.Janitor;
-import org.killbill.billing.payment.invoice.InvoicePaymentControlPluginApi;
-import org.killbill.billing.util.UUIDs;
-import org.killbill.billing.util.callcontext.CallContext;
 import org.killbill.billing.util.callcontext.CallOrigin;
 import org.killbill.billing.util.callcontext.InternalCallContextFactory;
 import org.killbill.billing.util.callcontext.UserType;
@@ -48,37 +41,32 @@ import org.killbill.billing.util.config.definition.PaymentConfig;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import com.google.common.base.Predicate;
-import com.google.common.collect.Iterables;
-import com.google.common.collect.Lists;
+import com.google.common.collect.ImmutableList;
 import com.google.common.eventbus.AllowConcurrentEvents;
 import com.google.common.eventbus.Subscribe;
 import com.google.inject.Inject;
 
-import static org.killbill.billing.payment.logging.PaymentLoggingHelper.logEnterAPICall;
-import static org.killbill.billing.payment.logging.PaymentLoggingHelper.logExitAPICall;
-
 public class PaymentBusEventHandler {
 
     private static final Logger log = LoggerFactory.getLogger(PaymentBusEventHandler.class);
 
     private final AccountInternalApi accountApi;
+    private final InvoicePaymentInternalApi invoicePaymentInternalApi;
     private final InternalCallContextFactory internalCallContextFactory;
-    private final PluginControlPaymentProcessor pluginControlPaymentProcessor;
     private final PaymentConfig paymentConfig;
     private final Janitor janitor;
 
     @Inject
     public PaymentBusEventHandler(final PaymentConfig paymentConfig,
                                   final AccountInternalApi accountApi,
-                                  final PluginControlPaymentProcessor pluginControlPaymentProcessor,
+                                  final InvoicePaymentInternalApi invoicePaymentInternalApi,
                                   final Janitor janitor,
                                   final InternalCallContextFactory internalCallContextFactory) {
         this.paymentConfig = paymentConfig;
         this.accountApi = accountApi;
+        this.invoicePaymentInternalApi = invoicePaymentInternalApi;
         this.janitor = janitor;
         this.internalCallContextFactory = internalCallContextFactory;
-        this.pluginControlPaymentProcessor = pluginControlPaymentProcessor;
     }
 
     @AllowConcurrentEvents
@@ -92,40 +80,38 @@ public class PaymentBusEventHandler {
     public void processInvoiceEvent(final InvoiceCreationInternalEvent event) {
         log.info("Received invoice creation notification for accountId='{}', invoiceId='{}'", event.getAccountId(), event.getInvoiceId());
 
-        final Collection<PluginProperty> properties = new ArrayList<PluginProperty>();
-        final PluginProperty propertyInvoiceId = new PluginProperty(InvoicePaymentControlPluginApi.PROP_IPCD_INVOICE_ID, event.getInvoiceId().toString(), false);
-        properties.add(propertyInvoiceId);
-
         final InternalCallContext internalContext = internalCallContextFactory.createInternalCallContext(event.getSearchKey2(), event.getSearchKey1(), "PaymentRequestProcessor", CallOrigin.INTERNAL, UserType.SYSTEM, event.getUserToken());
-        final CallContext callContext = internalCallContextFactory.createCallContext(internalContext);
 
         final BigDecimal amountToBePaid = null; // We let the plugin compute how much should be paid
         final List<String> paymentControlPluginNames = paymentConfig.getPaymentControlPluginNames(internalContext) != null ? new LinkedList<String>(paymentConfig.getPaymentControlPluginNames(internalContext)) : new LinkedList<String>();
-        paymentControlPluginNames.add(InvoicePaymentControlPluginApi.PLUGIN_NAME);
 
-        final String transactionType = TransactionType.PURCHASE.name();
-        Account account = null;
-        Payment payment = null;
-        PaymentTransaction paymentTransaction = null;
+        final Account account;
         try {
             account = accountApi.getAccountById(event.getAccountId(), internalContext);
 
-            logEnterAPICall(log,
-                            transactionType,
-                            account,
-                            account.getPaymentMethodId(),
-                            null,
-                            null,
-                            amountToBePaid,
-                            account.getCurrency(),
-                            null,
-                            null,
-                            null,
-                            paymentControlPluginNames);
-
-            payment = pluginControlPaymentProcessor.createPurchase(false, account, account.getPaymentMethodId(), null, amountToBePaid, account.getCurrency(), null, null, null, properties, paymentControlPluginNames, callContext, internalContext);
-
-            paymentTransaction = payment.getTransactions().get(payment.getTransactions().size() - 1);
+            invoicePaymentInternalApi.createPurchaseForInvoicePayment(false,
+                                                                      account,
+                                                                      event.getInvoiceId(),
+                                                                      account.getPaymentMethodId(),
+                                                                      null,
+                                                                      amountToBePaid,
+                                                                      account.getCurrency(),
+                                                                      null,
+                                                                      null,
+                                                                      null,
+                                                                      ImmutableList.<PluginProperty>of(),
+                                                                      new PaymentOptions() {
+                                                                   @Override
+                                                                   public boolean isExternalPayment() {
+                                                                       return false;
+                                                                   }
+
+                                                                   @Override
+                                                                   public List<String> getPaymentControlPluginNames() {
+                                                                       return paymentControlPluginNames;
+                                                                   }
+                                                               },
+                                                                      internalContext);
         } catch (final AccountApiException e) {
             log.warn("Failed to process invoice payment", e);
         } catch (final PaymentApiException e) {
@@ -133,20 +119,6 @@ public class PaymentBusEventHandler {
             if (e.getCode() != ErrorCode.PAYMENT_PLUGIN_API_ABORTED.getCode()) {
                 log.warn("Failed to process invoice payment {}", e.toString());
             }
-        } finally {
-            logExitAPICall(log,
-                           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,
-                           paymentControlPluginNames,
-                           null);
         }
     }
 }
diff --git a/payment/src/main/java/org/killbill/billing/payment/glue/PaymentModule.java b/payment/src/main/java/org/killbill/billing/payment/glue/PaymentModule.java
index 7586a8b..d9d06b0 100644
--- a/payment/src/main/java/org/killbill/billing/payment/glue/PaymentModule.java
+++ b/payment/src/main/java/org/killbill/billing/payment/glue/PaymentModule.java
@@ -1,7 +1,7 @@
 /*
  * Copyright 2010-2014 Ning, Inc.
- * Copyright 2014-2017 Groupon, Inc
- * Copyright 2014-2017 The Billing Project, LLC
+ * Copyright 2014-2018 Groupon, Inc
+ * Copyright 2014-2018 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
@@ -23,14 +23,19 @@ import javax.inject.Provider;
 import org.killbill.automaton.DefaultStateMachineConfig;
 import org.killbill.automaton.StateMachineConfig;
 import org.killbill.billing.control.plugin.api.PaymentControlPluginApi;
+import org.killbill.billing.invoice.api.InvoiceInternalApi;
 import org.killbill.billing.osgi.api.OSGIServiceRegistration;
 import org.killbill.billing.payment.api.AdminPaymentApi;
 import org.killbill.billing.payment.api.DefaultAdminPaymentApi;
+import org.killbill.billing.payment.api.DefaultInvoicePaymentApi;
 import org.killbill.billing.payment.api.DefaultPaymentApi;
 import org.killbill.billing.payment.api.DefaultPaymentGatewayApi;
+import org.killbill.billing.payment.api.InvoicePaymentApi;
+import org.killbill.billing.payment.api.InvoicePaymentInternalApi;
 import org.killbill.billing.payment.api.PaymentApi;
 import org.killbill.billing.payment.api.PaymentGatewayApi;
 import org.killbill.billing.payment.api.PaymentService;
+import org.killbill.billing.payment.api.svcs.DefaultInvoicePaymentInternalApi;
 import org.killbill.billing.payment.bus.PaymentBusEventHandler;
 import org.killbill.billing.payment.config.MultiTenantPaymentConfig;
 import org.killbill.billing.payment.caching.EhCacheStateMachineConfigCache;
@@ -151,6 +156,8 @@ public class PaymentModule extends KillBillModule {
         bind(PaymentPluginServiceRegistration.class).asEagerSingleton();
 
         bind(PaymentApi.class).to(DefaultPaymentApi.class).asEagerSingleton();
+        bind(InvoicePaymentApi.class).to(DefaultInvoicePaymentApi.class).asEagerSingleton();
+        bind(InvoicePaymentInternalApi.class).to(DefaultInvoicePaymentInternalApi.class).asEagerSingleton();
         bind(PaymentGatewayApi.class).to(DefaultPaymentGatewayApi.class).asEagerSingleton();
         bind(AdminPaymentApi.class).to(DefaultAdminPaymentApi.class).asEagerSingleton();
         bind(PaymentBusEventHandler.class).asEagerSingleton();
diff --git a/payment/src/main/java/org/killbill/billing/payment/invoice/InvoicePaymentControlPluginApi.java b/payment/src/main/java/org/killbill/billing/payment/invoice/InvoicePaymentControlPluginApi.java
index d87a2f0..ecb7dd2 100644
--- a/payment/src/main/java/org/killbill/billing/payment/invoice/InvoicePaymentControlPluginApi.java
+++ b/payment/src/main/java/org/killbill/billing/payment/invoice/InvoicePaymentControlPluginApi.java
@@ -1,6 +1,6 @@
 /*
- * Copyright 2014-2017 Groupon, Inc
- * Copyright 2014-2017 The Billing Project, LLC
+ * Copyright 2014-2018 Groupon, Inc
+ * Copyright 2014-2018 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
@@ -88,12 +88,12 @@ public final class InvoicePaymentControlPluginApi implements PaymentControlPlugi
 
     public static final String CREATED_BY = "InvoicePaymentControlPluginApi";
 
-    /* Don't change value String for properties as they are referenced from jaxrs without the constants which are not accessible */
     public static final String PLUGIN_NAME = "__INVOICE_PAYMENT_CONTROL_PLUGIN__";
-    public static final String PROP_IPCD_INVOICE_ID = "IPCD_INVOICE_ID";
-    public static final String PROP_IPCD_REFUND_IDS_WITH_AMOUNT_KEY = "IPCD_REFUND_IDS_AMOUNTS";
-    public static final String PROP_IPCD_REFUND_WITH_ADJUSTMENTS = "IPCD_REFUND_WITH_ADJUSTMENTS";
-    public static final String PROP_IPCD_PAYMENT_ID = "IPCD_PAYMENT_ID";
+
+    private static final String PROP_IPCD_INVOICE_ID = "IPCD_INVOICE_ID";
+    private static final String PROP_IPCD_REFUND_IDS_WITH_AMOUNT_KEY = "IPCD_REFUND_IDS_AMOUNTS";
+    private static final String PROP_IPCD_REFUND_WITH_ADJUSTMENTS = "IPCD_REFUND_WITH_ADJUSTMENTS";
+    private static final String PROP_IPCD_PAYMENT_ID = "IPCD_PAYMENT_ID";
 
     private final PaymentConfig paymentConfig;
     private final InvoiceInternalApi invoiceApi;
@@ -198,7 +198,7 @@ public final class InvoicePaymentControlPluginApi implements PaymentControlPlugi
                 case REFUND:
                     final Map<UUID, BigDecimal> idWithAmount = extractIdsWithAmountFromProperties(pluginProperties);
                     final PluginProperty prop = getPluginProperty(pluginProperties, PROP_IPCD_REFUND_WITH_ADJUSTMENTS);
-                    final boolean isAdjusted = prop != null ? Boolean.valueOf((String) prop.getValue()) : false;
+                    final boolean isAdjusted = prop != null && prop.getValue() != null ? Boolean.valueOf(prop.getValue().toString()) : false;
                     invoiceApi.recordRefund(paymentControlContext.getPaymentId(), paymentControlContext.getAmount(), isAdjusted, idWithAmount, paymentControlContext.getTransactionExternalKey(), internalContext);
                     break;
 
@@ -230,7 +230,7 @@ public final class InvoicePaymentControlPluginApi implements PaymentControlPlugi
                 case CREDIT:
                     final Map<UUID, BigDecimal> idWithAmountMap = extractIdsWithAmountFromProperties(pluginProperties);
                     final PluginProperty properties = getPluginProperty(pluginProperties, PROP_IPCD_REFUND_WITH_ADJUSTMENTS);
-                    final boolean isInvoiceAdjusted = properties != null ? Boolean.valueOf((String) properties.getValue()) : false;
+                    final boolean isInvoiceAdjusted = properties != null && properties.getValue() != null ? Boolean.valueOf(properties.getValue().toString()) : false;
 
                     final PluginProperty legacyPayment = getPluginProperty(pluginProperties, PROP_IPCD_PAYMENT_ID);
                     final UUID paymentId = legacyPayment != null ? (UUID) legacyPayment.getValue() : paymentControlContext.getPaymentId();
@@ -416,7 +416,7 @@ public final class InvoicePaymentControlPluginApi implements PaymentControlPlugi
         }
 
         final PluginProperty prop = getPluginProperty(pluginProperties, PROP_IPCD_REFUND_WITH_ADJUSTMENTS);
-        final boolean isAdjusted = prop != null ? Boolean.valueOf((String) prop.getValue()) : false;
+        final boolean isAdjusted = prop != null && prop.getValue() != null ? Boolean.valueOf(prop.getValue().toString()) : false;
         if (isAdjusted) {
             try {
                 invoiceApi.validateInvoiceItemAdjustments(paymentControlPluginContext.getPaymentId(), idWithAmount, internalContext);
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 5d45b6c..ec94497 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
@@ -1,7 +1,7 @@
 /*
  * Copyright 2010-2013 Ning, Inc.
- * Copyright 2014-2016 Groupon, Inc
- * Copyright 2014-2016 The Billing Project, LLC
+ * Copyright 2014-2018 Groupon, Inc
+ * Copyright 2014-2018 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
@@ -26,10 +26,8 @@ import java.util.List;
 import java.util.UUID;
 
 import javax.annotation.Nullable;
-import javax.annotation.concurrent.Immutable;
 
 import org.joda.time.LocalDate;
-import org.joda.time.LocalDate.Property;
 import org.killbill.billing.ErrorCode;
 import org.killbill.billing.account.api.Account;
 import org.killbill.billing.catalog.api.Currency;
@@ -868,9 +866,19 @@ public class TestPaymentApi extends PaymentTestSuiteWithEmbeddedDB {
                                                             requestedAmount,
                                                             new BigDecimal("1.0"),
                                                             Currency.USD));
-        final Payment payment = paymentApi.createPurchaseWithPaymentControl(account, account.getPaymentMethodId(), null, requestedAmount, Currency.USD, null, paymentExternalKey, transactionExternalKey,
-                                                                            createPropertiesForInvoice(invoice), INVOICE_PAYMENT, callContext);
-
+        invoicePaymentApi.createPurchaseForInvoicePayment(account,
+                                                          invoice.getId(),
+                                                          account.getPaymentMethodId(),
+                                                          null,
+                                                          requestedAmount,
+                                                          Currency.USD,
+                                                          null,
+                                                          paymentExternalKey,
+                                                          transactionExternalKey,
+                                                          ImmutableList.<PluginProperty>of(),
+                                                          INVOICE_PAYMENT,
+                                                          callContext);
+        final Payment payment = paymentApi.getPaymentByExternalKey(paymentExternalKey, false, false, ImmutableList.<PluginProperty>of(), callContext);
         assertEquals(payment.getExternalKey(), paymentExternalKey);
         assertEquals(payment.getPaymentMethodId(), account.getPaymentMethodId());
         assertEquals(payment.getAccountId(), account.getId());
@@ -899,7 +907,7 @@ public class TestPaymentApi extends PaymentTestSuiteWithEmbeddedDB {
     }
 
     @Test(groups = "slow")
-    public void testCreatePurchaseWithExternalKeyOverLimit() throws PaymentApiException, InvoiceApiException, EventBusException {
+    public void testCreatePurchaseWithExternalKeyOverLimit() throws InvoiceApiException, EventBusException {
         final BigDecimal requestedAmount = BigDecimal.TEN;
         final LocalDate now = clock.getUTCToday();
 
@@ -909,13 +917,22 @@ public class TestPaymentApi extends PaymentTestSuiteWithEmbeddedDB {
         final String transactionExternalKey = "Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Donec quam felis, ultricies nec, pellentesque eu, pretium quis,.";
 
         try {
-            paymentApi.createPurchaseWithPaymentControl(account, account.getPaymentMethodId(), null, requestedAmount, Currency.USD, null, paymentExternalKey, transactionExternalKey,
-                                                        createPropertiesForInvoice(invoice), INVOICE_PAYMENT, callContext);
+            invoicePaymentApi.createPurchaseForInvoicePayment(account,
+                                                              invoice.getId(),
+                                                              account.getPaymentMethodId(),
+                                                              null,
+                                                              requestedAmount,
+                                                              Currency.USD,
+                                                              null,
+                                                              paymentExternalKey,
+                                                              transactionExternalKey,
+                                                              ImmutableList.<PluginProperty>of(),
+                                                              INVOICE_PAYMENT,
+                                                              callContext);
             Assert.fail();
         } catch (final PaymentApiException e) {
             assertEquals(e.getCode(), ErrorCode.EXTERNAL_KEY_LIMIT_EXCEEDED.getCode());
         }
-
     }
 
     @Test(groups = "slow")
@@ -943,8 +960,18 @@ public class TestPaymentApi extends PaymentTestSuiteWithEmbeddedDB {
                                                             new BigDecimal("1.0"),
                                                             Currency.USD));
         try {
-            paymentApi.createPurchaseWithPaymentControl(account, account.getPaymentMethodId(), null, requestedAmount, Currency.USD, null, paymentExternalKey, transactionExternalKey,
-                                                        createPropertiesForInvoice(invoice), INVOICE_PAYMENT, callContext);
+            invoicePaymentApi.createPurchaseForInvoicePayment(account,
+                                                              invoice.getId(),
+                                                              account.getPaymentMethodId(),
+                                                              null,
+                                                              requestedAmount,
+                                                              Currency.USD,
+                                                              null,
+                                                              paymentExternalKey,
+                                                              transactionExternalKey,
+                                                              ImmutableList.<PluginProperty>of(),
+                                                              INVOICE_PAYMENT,
+                                                              callContext);
         } catch (final PaymentApiException expected) {
             assertTrue(true);
         }
@@ -1000,8 +1027,18 @@ public class TestPaymentApi extends PaymentTestSuiteWithEmbeddedDB {
                                                             new BigDecimal("1.0"),
                                                             Currency.USD));
         try {
-            paymentApi.createPurchaseWithPaymentControl(account, account.getPaymentMethodId(), null, requestedAmount, Currency.USD, null, paymentExternalKey, transactionExternalKey,
-                                                        createPropertiesForInvoice(invoice), INVOICE_PAYMENT, callContext);
+            invoicePaymentApi.createPurchaseForInvoicePayment(account,
+                                                              invoice.getId(),
+                                                              account.getPaymentMethodId(),
+                                                              null,
+                                                              requestedAmount,
+                                                              Currency.USD,
+                                                              null,
+                                                              paymentExternalKey,
+                                                              transactionExternalKey,
+                                                              ImmutableList.<PluginProperty>of(),
+                                                              INVOICE_PAYMENT,
+                                                              callContext);
         } catch (final PaymentApiException expected) {
             assertTrue(true);
         }
@@ -1031,9 +1068,18 @@ public class TestPaymentApi extends PaymentTestSuiteWithEmbeddedDB {
         assertEquals(payment.getTransactions().get(0).getTransactionType(), TransactionType.PURCHASE);
 
         // Make sure we can retry and that works
-        paymentApi.createPurchaseWithPaymentControl(account, account.getPaymentMethodId(), null, requestedAmount, Currency.USD, null, paymentExternalKey, transactionExternalKey,
-                                                    createPropertiesForInvoice(invoice), INVOICE_PAYMENT, callContext);
-
+        invoicePaymentApi.createPurchaseForInvoicePayment(account,
+                                                          invoice.getId(),
+                                                          account.getPaymentMethodId(),
+                                                          null,
+                                                          requestedAmount,
+                                                          Currency.USD,
+                                                          null,
+                                                          paymentExternalKey,
+                                                          transactionExternalKey,
+                                                          ImmutableList.<PluginProperty>of(),
+                                                          INVOICE_PAYMENT,
+                                                          callContext);
 
         final List<Payment> accountPayments2 = paymentApi.getAccountPayments(account.getId(), false, false, ImmutableList.<PluginProperty>of(), callContext);
         assertEquals(accountPayments2.size(), 1);
@@ -1075,8 +1121,18 @@ public class TestPaymentApi extends PaymentTestSuiteWithEmbeddedDB {
                                                             Currency.USD));
 
         try {
-            paymentApi.createPurchaseWithPaymentControl(account, account.getPaymentMethodId(), null, requestedAmount, Currency.USD, null, paymentExternalKey, transactionExternalKey,
-                                                        createPropertiesForInvoice(invoice), INVOICE_PAYMENT, callContext);
+            invoicePaymentApi.createPurchaseForInvoicePayment(account,
+                                                              invoice.getId(),
+                                                              account.getPaymentMethodId(),
+                                                              null,
+                                                              requestedAmount,
+                                                              Currency.USD,
+                                                              null,
+                                                              paymentExternalKey,
+                                                              transactionExternalKey,
+                                                              ImmutableList.<PluginProperty>of(),
+                                                              INVOICE_PAYMENT,
+                                                              callContext);
             Assert.fail("Unexpected success");
         } catch (final PaymentApiException e) {
         }
@@ -1107,8 +1163,19 @@ public class TestPaymentApi extends PaymentTestSuiteWithEmbeddedDB {
                                                                      Currency.USD);
         invoice.addInvoiceItem(invoiceItem);
 
-        final Payment payment = paymentApi.createPurchaseWithPaymentControl(account, account.getPaymentMethodId(), null, requestedAmount, Currency.USD, null, paymentExternalKey, transactionExternalKey,
-                                                                            createPropertiesForInvoice(invoice), INVOICE_PAYMENT, callContext);
+        invoicePaymentApi.createPurchaseForInvoicePayment(account,
+                                                          invoice.getId(),
+                                                          account.getPaymentMethodId(),
+                                                          null,
+                                                          requestedAmount,
+                                                          Currency.USD,
+                                                          null,
+                                                          paymentExternalKey,
+                                                          transactionExternalKey,
+                                                          ImmutableList.<PluginProperty>of(),
+                                                          INVOICE_PAYMENT,
+                                                          callContext);
+        final Payment payment = paymentApi.getPaymentByExternalKey(paymentExternalKey, false, false, ImmutableList.<PluginProperty>of(), callContext);
 
         final List<PluginProperty> refundProperties = ImmutableList.<PluginProperty>of();
         final Payment payment2 = paymentApi.createRefundWithPaymentControl(account, payment.getId(), requestedAmount, Currency.USD, null, transactionExternalKey2,
@@ -1150,8 +1217,19 @@ public class TestPaymentApi extends PaymentTestSuiteWithEmbeddedDB {
                                                                      Currency.USD);
         invoice.addInvoiceItem(invoiceItem);
 
-        final Payment payment = paymentApi.createPurchaseWithPaymentControl(account, account.getPaymentMethodId(), null, requestedAmount, Currency.USD, null, paymentExternalKey, transactionExternalKey,
-                                                                            createPropertiesForInvoice(invoice), INVOICE_PAYMENT, callContext);
+        invoicePaymentApi.createPurchaseForInvoicePayment(account,
+                                                          invoice.getId(),
+                                                          account.getPaymentMethodId(),
+                                                          null,
+                                                          requestedAmount,
+                                                          Currency.USD,
+                                                          null,
+                                                          paymentExternalKey,
+                                                          transactionExternalKey,
+                                                          ImmutableList.<PluginProperty>of(),
+                                                          INVOICE_PAYMENT,
+                                                          callContext);
+        final Payment payment = paymentApi.getPaymentByExternalKey(paymentExternalKey, false, false, ImmutableList.<PluginProperty>of(), callContext);
 
         final List<PluginProperty> refundProperties = ImmutableList.<PluginProperty>of();
 
@@ -1188,17 +1266,27 @@ public class TestPaymentApi extends PaymentTestSuiteWithEmbeddedDB {
                                                                      Currency.USD);
         invoice.addInvoiceItem(invoiceItem);
 
-        final Payment payment = paymentApi.createPurchaseWithPaymentControl(account, account.getPaymentMethodId(), null, requestedAmount, Currency.USD, null, paymentExternalKey, transactionExternalKey,
-                                                                            createPropertiesForInvoice(invoice), INVOICE_PAYMENT, callContext);
+        invoicePaymentApi.createPurchaseForInvoicePayment(account,
+                                                          invoice.getId(),
+                                                          account.getPaymentMethodId(),
+                                                          null,
+                                                          requestedAmount,
+                                                          Currency.USD,
+                                                          null,
+                                                          paymentExternalKey,
+                                                          transactionExternalKey,
+                                                          ImmutableList.<PluginProperty>of(),
+                                                          INVOICE_PAYMENT,
+                                                          callContext);
+        final Payment payment = paymentApi.getPaymentByExternalKey(paymentExternalKey, false, false, ImmutableList.<PluginProperty>of(), callContext);
 
         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.PROP_IPCD_REFUND_IDS_WITH_AMOUNT_KEY, uuidBigDecimalHashMap, false);
-        refundProperties.add(refundIdsProp);
 
-        final Payment payment2 = paymentApi.createRefundWithPaymentControl(account, payment.getId(), null, Currency.USD, null, transactionExternalKey2,
-                                                                           refundProperties, INVOICE_PAYMENT, callContext);
+        invoicePaymentApi.createRefundForInvoicePayment(true, uuidBigDecimalHashMap, account, payment.getId(), null, Currency.USD, null, transactionExternalKey2,
+                                                        refundProperties, INVOICE_PAYMENT, callContext);
+        final Payment payment2 = paymentApi.getPayment(payment.getId(), false, false, ImmutableList.<PluginProperty>of(), callContext);
 
         assertEquals(payment2.getTransactions().size(), 2);
         assertEquals(payment2.getExternalKey(), paymentExternalKey);
@@ -2576,12 +2664,6 @@ public class TestPaymentApi extends PaymentTestSuiteWithEmbeddedDB {
         }
     }
 
-    private List<PluginProperty> createPropertiesForInvoice(final Invoice invoice) {
-        final List<PluginProperty> result = new ArrayList<PluginProperty>();
-        result.add(new PluginProperty(InvoicePaymentControlPluginApi.PROP_IPCD_INVOICE_ID, invoice.getId().toString(), false));
-        return result;
-    }
-
     // Search by a key supported by the search in MockPaymentProviderPlugin
     private void checkPaymentMethodPagination(final UUID paymentMethodId, final Long maxNbRecords, final boolean deleted) throws PaymentApiException {
         final Pagination<PaymentMethod> foundPaymentMethods = paymentApi.searchPaymentMethods(paymentMethodId.toString(), 0L, maxNbRecords + 1, false, ImmutableList.<PluginProperty>of(), callContext);
diff --git a/payment/src/test/java/org/killbill/billing/payment/api/TestPaymentApiNoDB.java b/payment/src/test/java/org/killbill/billing/payment/api/TestPaymentApiNoDB.java
index 2b41c96..6a83d74 100644
--- a/payment/src/test/java/org/killbill/billing/payment/api/TestPaymentApiNoDB.java
+++ b/payment/src/test/java/org/killbill/billing/payment/api/TestPaymentApiNoDB.java
@@ -1,7 +1,7 @@
 /*
  * Copyright 2010-2013 Ning, Inc.
- * Copyright 2014-2015 Groupon, Inc
- * Copyright 2014-2015 The Billing Project, LLC
+ * Copyright 2014-2018 Groupon, Inc
+ * Copyright 2014-2018 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
@@ -19,7 +19,6 @@
 package org.killbill.billing.payment.api;
 
 import java.math.BigDecimal;
-import java.util.ArrayList;
 import java.util.List;
 import java.util.UUID;
 
@@ -29,16 +28,14 @@ import org.killbill.billing.catalog.api.Currency;
 import org.killbill.billing.invoice.api.Invoice;
 import org.killbill.billing.payment.MockRecurringInvoiceItem;
 import org.killbill.billing.payment.PaymentTestSuiteNoDB;
-import org.killbill.billing.payment.invoice.InvoicePaymentControlPluginApi;
 import org.killbill.billing.payment.provider.DefaultNoOpPaymentMethodPlugin;
-import org.killbill.billing.payment.provider.MockPaymentProviderPlugin;
-import org.mockito.Mockito;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.testng.annotations.BeforeClass;
 import org.testng.annotations.BeforeMethod;
 import org.testng.annotations.Test;
 
+import com.google.common.base.MoreObjects;
 import com.google.common.collect.ImmutableList;
 
 import static org.testng.Assert.assertEquals;
@@ -49,17 +46,6 @@ import static org.testng.Assert.fail;
 public class TestPaymentApiNoDB extends PaymentTestSuiteNoDB {
 
     private static final Logger log = LoggerFactory.getLogger(TestPaymentApiNoDB.class);
-    private static final PaymentOptions PAYMENT_OPTIONS = new PaymentOptions() {
-        @Override
-        public boolean isExternalPayment() {
-            return false;
-        }
-
-        @Override
-        public List<String> getPaymentControlPluginNames() {
-            return ImmutableList.<String>of(InvoicePaymentControlPluginApi.PLUGIN_NAME);
-        }
-    };
 
     private final Iterable<PluginProperty> PLUGIN_PROPERTIES = ImmutableList.<PluginProperty>of();
     private Account account;
@@ -86,7 +72,7 @@ public class TestPaymentApiNoDB extends PaymentTestSuiteNoDB {
 
     @Test(groups = "fast")
     public void testSimpleInvoicePaymentWithNoAmount() throws Exception {
-        final BigDecimal invoiceAmount = new BigDecimal("10.0011");
+        final BigDecimal invoiceAmount = new BigDecimal("10");
         final BigDecimal requestedAmount = null;
         final BigDecimal expectedAmount = null;
 
@@ -138,22 +124,28 @@ public class TestPaymentApiNoDB extends PaymentTestSuiteNoDB {
                                                             Currency.USD));
 
         try {
-
-            final List<PluginProperty> properties = new ArrayList<PluginProperty>();
-            final PluginProperty prop1 = new PluginProperty(InvoicePaymentControlPluginApi.PROP_IPCD_INVOICE_ID, invoice.getId().toString(), false);
-            properties.add(prop1);
-
-            final Payment paymentInfo = paymentApi.createPurchaseWithPaymentControl(account, account.getPaymentMethodId(), null, requestedAmount, account.getCurrency(),  null,
-                                                                                    invoice.getId().toString(), UUID.randomUUID().toString(), properties, PAYMENT_OPTIONS, callContext);
-            if (expectedAmount == null) {
+            invoicePaymentApi.createPurchaseForInvoicePayment(account,
+                                                              invoice.getId(),
+                                                              account.getPaymentMethodId(),
+                                                              null,
+                                                              requestedAmount,
+                                                              account.getCurrency(),
+                                                              null,
+                                                              invoice.getId().toString(),
+                                                              UUID.randomUUID().toString(),
+                                                              ImmutableList.<PluginProperty>of(),
+                                                              PAYMENT_OPTIONS,
+                                                              callContext);
+            final Payment paymentInfo = paymentApi.getPaymentByExternalKey(invoice.getId().toString(), false, false, ImmutableList.<PluginProperty>of(), callContext);
+            if (requestedAmount != null && expectedAmount == null) {
                 fail("Expected to fail because requested amount > invoice amount");
             }
             assertNotNull(paymentInfo.getId());
-            assertTrue(paymentInfo.getPurchasedAmount().compareTo(expectedAmount) == 0);
+            assertTrue(paymentInfo.getPurchasedAmount().compareTo(MoreObjects.firstNonNull(expectedAmount, invoiceAmount)) == 0);
             assertNotNull(paymentInfo.getPaymentNumber());
             assertEquals(paymentInfo.getExternalKey(), invoice.getId().toString());
             assertEquals(paymentInfo.getCurrency(), Currency.USD);
-            assertTrue(paymentInfo.getTransactions().get(0).getAmount().compareTo(expectedAmount) == 0);
+            assertTrue(paymentInfo.getTransactions().get(0).getAmount().compareTo(MoreObjects.firstNonNull(expectedAmount, invoiceAmount)) == 0);
             assertEquals(paymentInfo.getTransactions().get(0).getCurrency(), Currency.USD);
             assertEquals(paymentInfo.getTransactions().get(0).getPaymentId(), paymentInfo.getId());
             assertEquals(paymentInfo.getTransactions().get(0).getTransactionType(), TransactionType.PURCHASE);
diff --git a/payment/src/test/java/org/killbill/billing/payment/PaymentTestSuiteNoDB.java b/payment/src/test/java/org/killbill/billing/payment/PaymentTestSuiteNoDB.java
index c429872..ed3df88 100644
--- a/payment/src/test/java/org/killbill/billing/payment/PaymentTestSuiteNoDB.java
+++ b/payment/src/test/java/org/killbill/billing/payment/PaymentTestSuiteNoDB.java
@@ -1,7 +1,7 @@
 /*
  * Copyright 2010-2013 Ning, Inc.
- * Copyright 2014-2017 Groupon, Inc
- * Copyright 2014-2017 The Billing Project, LLC
+ * Copyright 2014-2018 Groupon, Inc
+ * Copyright 2014-2018 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
@@ -18,14 +18,19 @@
 
 package org.killbill.billing.payment;
 
+import java.util.List;
+
 import javax.inject.Named;
 
 import org.killbill.billing.GuicyKillbillTestSuiteNoDB;
 import org.killbill.billing.account.api.AccountInternalApi;
 import org.killbill.billing.invoice.api.InvoiceInternalApi;
 import org.killbill.billing.osgi.api.OSGIServiceRegistration;
+import org.killbill.billing.payment.api.InvoicePaymentApi;
+import org.killbill.billing.payment.api.InvoicePaymentInternalApi;
 import org.killbill.billing.payment.api.PaymentApi;
 import org.killbill.billing.payment.api.PaymentGatewayApi;
+import org.killbill.billing.payment.api.PaymentOptions;
 import org.killbill.billing.payment.caching.StateMachineConfigCache;
 import org.killbill.billing.payment.core.PaymentExecutors;
 import org.killbill.billing.payment.core.PaymentMethodProcessor;
@@ -52,6 +57,7 @@ import org.testng.annotations.AfterMethod;
 import org.testng.annotations.BeforeClass;
 import org.testng.annotations.BeforeMethod;
 
+import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
 import com.google.inject.Guice;
 import com.google.inject.Inject;
@@ -76,6 +82,10 @@ public abstract class PaymentTestSuiteNoDB extends GuicyKillbillTestSuiteNoDB {
     @Inject
     protected PaymentApi paymentApi;
     @Inject
+    protected InvoicePaymentApi invoicePaymentApi;
+    @Inject
+    protected InvoicePaymentInternalApi invoicePaymentInternalApi;
+    @Inject
     protected PaymentGatewayApi paymentGatewayApi;
     @Inject
     protected AccountInternalApi accountInternalApi;
@@ -105,6 +115,18 @@ public abstract class PaymentTestSuiteNoDB extends GuicyKillbillTestSuiteNoDB {
     @Inject
     protected TenantInternalApi tenantInternalApi;
 
+    protected static final PaymentOptions PAYMENT_OPTIONS = new PaymentOptions() {
+        @Override
+        public boolean isExternalPayment() {
+            return false;
+        }
+
+        @Override
+        public List<String> getPaymentControlPluginNames() {
+            return ImmutableList.<String>of();
+        }
+    };
+
     @Override
     protected KillbillConfigSource getConfigSource() {
         return getConfigSource("/payment.properties",
diff --git a/payment/src/test/java/org/killbill/billing/payment/PaymentTestSuiteWithEmbeddedDB.java b/payment/src/test/java/org/killbill/billing/payment/PaymentTestSuiteWithEmbeddedDB.java
index 61dca40..82ee233 100644
--- a/payment/src/test/java/org/killbill/billing/payment/PaymentTestSuiteWithEmbeddedDB.java
+++ b/payment/src/test/java/org/killbill/billing/payment/PaymentTestSuiteWithEmbeddedDB.java
@@ -1,7 +1,7 @@
 /*
  * Copyright 2010-2013 Ning, Inc.
- * Copyright 2014-2017 Groupon, Inc
- * Copyright 2014-2017 The Billing Project, LLC
+ * Copyright 2014-2018 Groupon, Inc
+ * Copyright 2014-2018 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
@@ -24,6 +24,7 @@ import org.killbill.billing.control.plugin.api.PaymentControlPluginApi;
 import org.killbill.billing.invoice.api.InvoiceInternalApi;
 import org.killbill.billing.osgi.api.OSGIServiceRegistration;
 import org.killbill.billing.payment.api.AdminPaymentApi;
+import org.killbill.billing.payment.api.InvoicePaymentApi;
 import org.killbill.billing.payment.api.PaymentApi;
 import org.killbill.billing.payment.api.PaymentGatewayApi;
 import org.killbill.billing.payment.caching.StateMachineConfigCache;
@@ -82,6 +83,8 @@ public abstract class PaymentTestSuiteWithEmbeddedDB extends GuicyKillbillTestSu
     @Inject
     protected PaymentApi paymentApi;
     @Inject
+    protected InvoicePaymentApi invoicePaymentApi;
+    @Inject
     protected AdminPaymentApi adminPaymentApi;
     @Inject
     protected PaymentGatewayApi paymentGatewayApi;
diff --git a/payment/src/test/java/org/killbill/billing/payment/TestJanitor.java b/payment/src/test/java/org/killbill/billing/payment/TestJanitor.java
index 704250d..7a9a1e1 100644
--- a/payment/src/test/java/org/killbill/billing/payment/TestJanitor.java
+++ b/payment/src/test/java/org/killbill/billing/payment/TestJanitor.java
@@ -22,7 +22,6 @@ import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.Iterator;
 import java.util.List;
-import java.util.Map;
 import java.util.UUID;
 import java.util.concurrent.Callable;
 import java.util.concurrent.TimeUnit;
@@ -47,7 +46,6 @@ import org.killbill.billing.payment.api.TransactionType;
 import org.killbill.billing.payment.bus.PaymentBusEventHandler;
 import org.killbill.billing.payment.core.janitor.Janitor;
 import org.killbill.billing.payment.dao.PaymentAttemptModelDao;
-import org.killbill.billing.payment.dao.PaymentModelDao;
 import org.killbill.billing.payment.dao.PaymentTransactionModelDao;
 import org.killbill.billing.payment.glue.DefaultPaymentService;
 import org.killbill.billing.payment.invoice.InvoicePaymentControlPluginApi;
@@ -68,8 +66,6 @@ import org.killbill.notificationq.api.NotificationEventWithMetadata;
 import org.killbill.notificationq.api.NotificationQueueService;
 import org.killbill.notificationq.api.NotificationQueueService.NoSuchNotificationQueue;
 import org.skife.config.TimeSpan;
-import org.skife.jdbi.v2.Handle;
-import org.skife.jdbi.v2.tweak.HandleCallback;
 import org.testng.Assert;
 import org.testng.annotations.AfterMethod;
 import org.testng.annotations.BeforeClass;
@@ -157,6 +153,10 @@ public class TestJanitor extends PaymentTestSuiteWithEmbeddedDB {
 
     @AfterMethod(groups = "slow")
     public void afterMethod() throws Exception {
+        if (hasFailed()) {
+            return;
+        }
+
         retryService.stop();
 
         eventBus.unregister(handler);
@@ -191,8 +191,19 @@ public class TestJanitor extends PaymentTestSuiteWithEmbeddedDB {
                                                             Currency.USD));
 
         testListener.pushExpectedEvent(NextEvent.PAYMENT);
-        final Payment payment = paymentApi.createPurchaseWithPaymentControl(account, account.getPaymentMethodId(), null, requestedAmount, Currency.USD, null, paymentExternalKey, transactionExternalKey,
-                                                                            createPropertiesForInvoice(invoice), INVOICE_PAYMENT, callContext);
+        invoicePaymentApi.createPurchaseForInvoicePayment(account,
+                                                          invoice.getId(),
+                                                          account.getPaymentMethodId(),
+                                                          null,
+                                                          requestedAmount,
+                                                          Currency.USD,
+                                                          null,
+                                                          paymentExternalKey,
+                                                          transactionExternalKey,
+                                                          ImmutableList.<PluginProperty>of(),
+                                                          INVOICE_PAYMENT,
+                                                          callContext);
+        final Payment payment = paymentApi.getPaymentByExternalKey(paymentExternalKey, false, false, ImmutableList.<PluginProperty>of(), callContext);
         testListener.assertListenerStatus();
         assertEquals(payment.getTransactions().size(), 1);
         assertEquals(payment.getTransactions().get(0).getTransactionStatus(), TransactionStatus.SUCCESS);
@@ -247,19 +258,29 @@ public class TestJanitor extends PaymentTestSuiteWithEmbeddedDB {
         invoice.addInvoiceItem(invoiceItem);
 
         testListener.pushExpectedEvent(NextEvent.PAYMENT);
-        final Payment payment = paymentApi.createPurchaseWithPaymentControl(account, account.getPaymentMethodId(), null, requestedAmount, Currency.USD, null, paymentExternalKey, transactionExternalKey,
-                                                                            createPropertiesForInvoice(invoice), INVOICE_PAYMENT, callContext);
+        invoicePaymentApi.createPurchaseForInvoicePayment(account,
+                                                          invoice.getId(),
+                                                          account.getPaymentMethodId(),
+                                                          null,
+                                                          requestedAmount,
+                                                          Currency.USD,
+                                                          null,
+                                                          paymentExternalKey,
+                                                          transactionExternalKey,
+                                                          ImmutableList.<PluginProperty>of(),
+                                                          INVOICE_PAYMENT,
+                                                          callContext);
+        final Payment payment = paymentApi.getPaymentByExternalKey(paymentExternalKey, false, false, ImmutableList.<PluginProperty>of(), callContext);
         testListener.assertListenerStatus();
 
         final List<PluginProperty> refundProperties = new ArrayList<PluginProperty>();
         final HashMap<UUID, BigDecimal> uuidBigDecimalHashMap = new HashMap<UUID, BigDecimal>();
         uuidBigDecimalHashMap.put(invoiceItem.getId(), new BigDecimal("1.0"));
-        final PluginProperty refundIdsProp = new PluginProperty(InvoicePaymentControlPluginApi.PROP_IPCD_REFUND_IDS_WITH_AMOUNT_KEY, uuidBigDecimalHashMap, false);
-        refundProperties.add(refundIdsProp);
 
         testListener.pushExpectedEvent(NextEvent.PAYMENT);
-        final Payment payment2 = paymentApi.createRefundWithPaymentControl(account, payment.getId(), null, Currency.USD, null, transactionExternalKey2,
-                                                                           refundProperties, INVOICE_PAYMENT, callContext);
+        invoicePaymentApi.createRefundForInvoicePayment(false, uuidBigDecimalHashMap, account, payment.getId(), null, Currency.USD, null, transactionExternalKey2,
+                                                        refundProperties, INVOICE_PAYMENT, callContext);
+        final Payment payment2 = paymentApi.getPayment(payment.getId(), false, false, refundProperties, callContext);
         testListener.assertListenerStatus();
 
         assertEquals(payment2.getTransactions().size(), 2);
@@ -527,12 +548,6 @@ public class TestJanitor extends PaymentTestSuiteWithEmbeddedDB {
                                          });
     }
 
-    private List<PluginProperty> createPropertiesForInvoice(final Invoice invoice) {
-        final List<PluginProperty> result = new ArrayList<PluginProperty>();
-        result.add(new PluginProperty(InvoicePaymentControlPluginApi.PROP_IPCD_INVOICE_ID, invoice.getId().toString(), false));
-        return result;
-    }
-
     private void assertNotificationsCompleted(final InternalCallContext internalCallContext, final long timeoutSec) {
         try {
             await().atMost(timeoutSec, SECONDS).until(new Callable<Boolean>() {
diff --git a/payment/src/test/java/org/killbill/billing/payment/TestPaymentHelper.java b/payment/src/test/java/org/killbill/billing/payment/TestPaymentHelper.java
index 5114550..0ecd4e3 100644
--- a/payment/src/test/java/org/killbill/billing/payment/TestPaymentHelper.java
+++ b/payment/src/test/java/org/killbill/billing/payment/TestPaymentHelper.java
@@ -30,6 +30,7 @@ import org.killbill.billing.account.api.AccountUserApi;
 import org.killbill.billing.account.api.ImmutableAccountInternalApi;
 import org.killbill.billing.callcontext.InternalCallContext;
 import org.killbill.billing.callcontext.InternalTenantContext;
+import org.killbill.billing.callcontext.MutableCallContext;
 import org.killbill.billing.callcontext.MutableInternalCallContext;
 import org.killbill.billing.catalog.api.Currency;
 import org.killbill.billing.events.InvoiceCreationInternalEvent;
@@ -148,14 +149,16 @@ public class TestPaymentHelper {
         Mockito.when(accountData.getCreatedDate()).thenReturn(clock.getUTCNow());
         Mockito.when(accountData.getReferenceTime()).thenReturn(clock.getUTCNow());
 
+        final MutableCallContext mutableCallContext = new MutableCallContext(internalCallContext);
+
         Account account;
         if (isFastTest()) {
-            account = GuicyKillbillTestSuiteNoDB.createMockAccount(accountData, accountApi, accountInternalApi, immutableAccountInternalApi, nonEntityDao, clock, internalCallContextFactory, context, internalCallContext);
+            account = GuicyKillbillTestSuiteNoDB.createMockAccount(accountData, accountApi, accountInternalApi, immutableAccountInternalApi, nonEntityDao, clock, internalCallContextFactory, mutableCallContext, internalCallContext);
         } else {
             account = accountApi.createAccount(accountData, context);
         }
 
-        GuicyKillbillTestSuite.refreshCallContext(account.getId(), clock, internalCallContextFactory, context, internalCallContext);
+        GuicyKillbillTestSuite.refreshCallContext(account.getId(), clock, internalCallContextFactory, mutableCallContext, internalCallContext);
 
         if (addPaymentMethod) {
             final PaymentMethodPlugin pm = new DefaultNoOpPaymentMethodPlugin(UUID.randomUUID().toString(), true, null);
diff --git a/payment/src/test/java/org/killbill/billing/payment/TestRetryService.java b/payment/src/test/java/org/killbill/billing/payment/TestRetryService.java
index 5d3ae1a..d1da4a7 100644
--- a/payment/src/test/java/org/killbill/billing/payment/TestRetryService.java
+++ b/payment/src/test/java/org/killbill/billing/payment/TestRetryService.java
@@ -1,7 +1,7 @@
 /*
  * Copyright 2010-2013 Ning, Inc.
- * Copyright 2014-2016 Groupon, Inc
- * Copyright 2014-2016 The Billing Project, LLC
+ * Copyright 2014-2018 Groupon, Inc
+ * Copyright 2014-2018 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
@@ -19,7 +19,6 @@
 package org.killbill.billing.payment;
 
 import java.math.BigDecimal;
-import java.util.ArrayList;
 import java.util.Collections;
 import java.util.Comparator;
 import java.util.List;
@@ -36,7 +35,6 @@ import org.killbill.billing.payment.api.PaymentApiException;
 import org.killbill.billing.payment.api.PluginProperty;
 import org.killbill.billing.payment.dao.PaymentAttemptModelDao;
 import org.killbill.billing.payment.dao.PaymentTransactionModelDao;
-import org.killbill.billing.payment.invoice.InvoicePaymentControlPluginApi;
 import org.killbill.billing.payment.provider.MockPaymentProviderPlugin;
 import org.testng.annotations.AfterMethod;
 import org.testng.annotations.BeforeMethod;
@@ -55,6 +53,7 @@ import static org.testng.Assert.assertTrue;
 public class TestRetryService extends PaymentTestSuiteNoDB {
 
     private static final int TIMEOUT = 10;
+    private static final ImmutableList<PluginProperty> NO_PROPERTIES = ImmutableList.<PluginProperty>of();
 
     private MockPaymentProviderPlugin mockPaymentProviderPlugin;
 
@@ -119,8 +118,19 @@ public class TestRetryService extends PaymentTestSuiteNoDB {
         final String paymentExternalKey = UUID.randomUUID().toString();
         final String transactionExternalKey = UUID.randomUUID().toString();
         try {
-            pluginControlPaymentProcessor.createPurchase(false, account, account.getPaymentMethodId(), null, amount, Currency.USD, null, paymentExternalKey, transactionExternalKey,
-                                                         createPropertiesForInvoice(invoice), ImmutableList.<String>of(InvoicePaymentControlPluginApi.PLUGIN_NAME), callContext, internalCallContext);
+            invoicePaymentInternalApi.createPurchaseForInvoicePayment(false,
+                                                                      account,
+                                                                      invoice.getId(),
+                                                                      account.getPaymentMethodId(),
+                                                                      null,
+                                                                      amount,
+                                                                      Currency.USD,
+                                                                      null,
+                                                                      paymentExternalKey,
+                                                                      transactionExternalKey,
+                                                                      NO_PROPERTIES,
+                                                                      PAYMENT_OPTIONS,
+                                                                      internalCallContext);
         } catch (final PaymentApiException e) {
             failed = true;
         }
@@ -164,8 +174,19 @@ public class TestRetryService extends PaymentTestSuiteNoDB {
 
         final String paymentExternalKey = UUID.randomUUID().toString();
         final String transactionExternalKey = UUID.randomUUID().toString();
-        pluginControlPaymentProcessor.createPurchase(false, account, account.getPaymentMethodId(), null, amount, Currency.USD, null, paymentExternalKey, transactionExternalKey,
-                                                     createPropertiesForInvoice(invoice), ImmutableList.<String>of(InvoicePaymentControlPluginApi.PLUGIN_NAME), callContext, internalCallContext);
+        invoicePaymentInternalApi.createPurchaseForInvoicePayment(false,
+                                                                  account,
+                                                                  invoice.getId(),
+                                                                  account.getPaymentMethodId(),
+                                                                  null,
+                                                                  amount,
+                                                                  Currency.USD,
+                                                                  null,
+                                                                  paymentExternalKey,
+                                                                  transactionExternalKey,
+                                                                  NO_PROPERTIES,
+                                                                  PAYMENT_OPTIONS,
+                                                                  internalCallContext);
 
         Payment payment = getPaymentForExternalKey(paymentExternalKey);
         List<PaymentAttemptModelDao> attempts = paymentDao.getPaymentAttempts(paymentExternalKey, internalCallContext);
@@ -237,8 +258,19 @@ public class TestRetryService extends PaymentTestSuiteNoDB {
 
         final String paymentExternalKey = UUID.randomUUID().toString();
         final String transactionExternalKey = UUID.randomUUID().toString();
-        pluginControlPaymentProcessor.createPurchase(false, account, account.getPaymentMethodId(), null, amount, Currency.USD, null, paymentExternalKey, transactionExternalKey,
-                                                     createPropertiesForInvoice(invoice), ImmutableList.<String>of(InvoicePaymentControlPluginApi.PLUGIN_NAME), callContext, internalCallContext);
+        invoicePaymentInternalApi.createPurchaseForInvoicePayment(false,
+                                                                  account,
+                                                                  invoice.getId(),
+                                                                  account.getPaymentMethodId(),
+                                                                  null,
+                                                                  amount,
+                                                                  Currency.USD,
+                                                                  null,
+                                                                  paymentExternalKey,
+                                                                  transactionExternalKey,
+                                                                  NO_PROPERTIES,
+                                                                  PAYMENT_OPTIONS,
+                                                                  internalCallContext);
 
         Payment payment = getPaymentForExternalKey(paymentExternalKey);
         List<PaymentAttemptModelDao> attempts = paymentDao.getPaymentAttempts(paymentExternalKey, internalCallContext);
@@ -320,8 +352,19 @@ public class TestRetryService extends PaymentTestSuiteNoDB {
 
         final String paymentExternalKey = UUID.randomUUID().toString();
         final String transactionExternalKey = UUID.randomUUID().toString();
-        pluginControlPaymentProcessor.createPurchase(false, account, account.getPaymentMethodId(), null, amount, Currency.USD, null, paymentExternalKey, transactionExternalKey,
-                                                     createPropertiesForInvoice(invoice), ImmutableList.<String>of(InvoicePaymentControlPluginApi.PLUGIN_NAME), callContext, internalCallContext);
+        invoicePaymentInternalApi.createPurchaseForInvoicePayment(false,
+                                                                  account,
+                                                                  invoice.getId(),
+                                                                  account.getPaymentMethodId(),
+                                                                  null,
+                                                                  amount,
+                                                                  Currency.USD,
+                                                                  null,
+                                                                  paymentExternalKey,
+                                                                  transactionExternalKey,
+                                                                  NO_PROPERTIES,
+                                                                  PAYMENT_OPTIONS,
+                                                                  internalCallContext);
 
         Payment payment = getPaymentForExternalKey(paymentExternalKey);
         List<PaymentAttemptModelDao> attempts = paymentDao.getPaymentAttempts(paymentExternalKey, internalCallContext);
@@ -393,20 +436,6 @@ public class TestRetryService extends PaymentTestSuiteNoDB {
         clock.addDays(nbDays);
     }
 
-    private int getMaxRetrySizeForFailureType(final FailureType failureType) {
-        if (failureType == FailureType.PAYMENT_FAILURE) {
-            return paymentConfig.getPaymentFailureRetryDays(internalCallContext).size();
-        } else {
-            return 0;
-        }
-    }
-
-    private List<PluginProperty> createPropertiesForInvoice(final Invoice invoice) {
-        final List<PluginProperty> result = new ArrayList<PluginProperty>();
-        result.add(new PluginProperty(InvoicePaymentControlPluginApi.PROP_IPCD_INVOICE_ID, invoice.getId().toString(), false));
-        return result;
-    }
-
     private enum FailureType {
         PLUGIN_EXCEPTION,
         PAYMENT_FAILURE

pom.xml 2(+1 -1)

diff --git a/pom.xml b/pom.xml
index 637645c..4a29a27 100644
--- a/pom.xml
+++ b/pom.xml
@@ -21,7 +21,7 @@
     <parent>
         <artifactId>killbill-oss-parent</artifactId>
         <groupId>org.kill-bill.billing</groupId>
-        <version>0.141.80</version>
+        <version>0.141.81-SNAPSHOT</version>
     </parent>
     <artifactId>killbill</artifactId>
     <version>0.19.17-SNAPSHOT</version>
diff --git a/util/src/test/java/org/killbill/billing/callcontext/MutableCallContext.java b/util/src/test/java/org/killbill/billing/callcontext/MutableCallContext.java
index 725df20..b79ffba 100644
--- a/util/src/test/java/org/killbill/billing/callcontext/MutableCallContext.java
+++ b/util/src/test/java/org/killbill/billing/callcontext/MutableCallContext.java
@@ -26,7 +26,7 @@ import org.killbill.billing.util.callcontext.UserType;
 
 public class MutableCallContext implements CallContext {
 
-    private final CallContext delegate;
+    private CallContext delegate;
     private DateTime createdDate;
 
     public MutableCallContext(final MutableInternalCallContext internalCallContext) {
@@ -34,6 +34,10 @@ public class MutableCallContext implements CallContext {
         this.createdDate = delegate.getCreatedDate();
     }
 
+    public void setDelegate(final UUID accountId, final MutableInternalCallContext internalCallContext) {
+        this.delegate = internalCallContext.toCallContext(accountId, null);
+    }
+
     @Override
     public UUID getUserToken() {
         return delegate.getUserToken();
diff --git a/util/src/test/java/org/killbill/billing/GuicyKillbillTestSuite.java b/util/src/test/java/org/killbill/billing/GuicyKillbillTestSuite.java
index a8f886a..f2b2a72 100644
--- a/util/src/test/java/org/killbill/billing/GuicyKillbillTestSuite.java
+++ b/util/src/test/java/org/killbill/billing/GuicyKillbillTestSuite.java
@@ -105,7 +105,7 @@ public class GuicyKillbillTestSuite implements IHookable {
     public static void refreshCallContext(final UUID accountId,
                                           final Clock clock,
                                           final InternalCallContextFactory internalCallContextFactory,
-                                          final TenantContext callContext,
+                                          final MutableCallContext callContext,
                                           final MutableInternalCallContext internalCallContext) {
         final InternalTenantContext tmp = internalCallContextFactory.createInternalTenantContext(accountId, callContext);
         internalCallContext.setAccountRecordId(tmp.getAccountRecordId());
@@ -113,6 +113,8 @@ public class GuicyKillbillTestSuite implements IHookable {
         internalCallContext.setReferenceTime(tmp.getReferenceLocalTime());
         internalCallContext.setCreatedDate(clock.getUTCNow());
         internalCallContext.setUpdatedDate(clock.getUTCNow());
+
+        callContext.setDelegate(accountId, internalCallContext);
     }
 
     protected KillbillConfigSource getConfigSource() {
diff --git a/util/src/test/java/org/killbill/billing/GuicyKillbillTestSuiteNoDB.java b/util/src/test/java/org/killbill/billing/GuicyKillbillTestSuiteNoDB.java
index a337c8f..06c00ad 100644
--- a/util/src/test/java/org/killbill/billing/GuicyKillbillTestSuiteNoDB.java
+++ b/util/src/test/java/org/killbill/billing/GuicyKillbillTestSuiteNoDB.java
@@ -27,6 +27,7 @@ import org.killbill.billing.account.api.AccountInternalApi;
 import org.killbill.billing.account.api.AccountUserApi;
 import org.killbill.billing.account.api.ImmutableAccountInternalApi;
 import org.killbill.billing.callcontext.InternalTenantContext;
+import org.killbill.billing.callcontext.MutableCallContext;
 import org.killbill.billing.callcontext.MutableInternalCallContext;
 import org.killbill.billing.dao.MockNonEntityDao;
 import org.killbill.billing.util.callcontext.CallContext;
@@ -44,7 +45,7 @@ public class GuicyKillbillTestSuiteNoDB extends GuicyKillbillTestSuite {
                                             final NonEntityDao nonEntityDao,
                                             final Clock clock,
                                             final InternalCallContextFactory internalCallContextFactory,
-                                            final CallContext callContext,
+                                            final MutableCallContext callContext,
                                             final MutableInternalCallContext internalCallContext) throws AccountApiException {
         final Account account = accountUserApi.createAccount(accountData, callContext);
 
diff --git a/util/src/test/java/org/killbill/billing/mock/glue/MockInvoiceModule.java b/util/src/test/java/org/killbill/billing/mock/glue/MockInvoiceModule.java
index 3824d12..eaa612a 100644
--- a/util/src/test/java/org/killbill/billing/mock/glue/MockInvoiceModule.java
+++ b/util/src/test/java/org/killbill/billing/mock/glue/MockInvoiceModule.java
@@ -1,7 +1,7 @@
 /*
  * Copyright 2010-2012 Ning, Inc.
- * Copyright 2014 Groupon, Inc
- * Copyright 2014 The Billing Project, LLC
+ * Copyright 2014-2018 Groupon, Inc
+ * Copyright 2014-2018 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
@@ -20,7 +20,6 @@ package org.killbill.billing.mock.glue;
 
 import org.killbill.billing.glue.InvoiceModule;
 import org.killbill.billing.invoice.api.InvoiceInternalApi;
-import org.killbill.billing.invoice.api.InvoicePaymentApi;
 import org.killbill.billing.invoice.api.InvoiceUserApi;
 import org.killbill.billing.platform.api.KillbillConfigSource;
 import org.killbill.billing.util.glue.KillBillModule;
@@ -38,15 +37,9 @@ public class MockInvoiceModule extends KillBillModule implements InvoiceModule {
     }
 
     @Override
-    public void installInvoicePaymentApi() {
-        bind(InvoicePaymentApi.class).toInstance(Mockito.mock(InvoicePaymentApi.class));
-    }
-
-    @Override
     protected void configure() {
         installInvoiceUserApi();
         installInvoiceInternalApi();
-        installInvoicePaymentApi();
     }
 
     @Override