Repository: killbill/killbill
Issue: DefaultPaymentGatewayApi doesn't respect isAborted flag

We should make sure 422 is returned if the HPP payment is aborted by a control plugin.


Changed files:
payment/src/main/java/org/killbill/billing/payment/api/DefaultPaymentGatewayApi.java
payment/src/main/java/org/killbill/billing/payment/core/sm/control/ControlPluginRunner.java
payment/src/main/java/org/killbill/billing/payment/core/sm/control/PaymentControlApiAbortException.java
payment/src/test/java/org/killbill/billing/payment/api/TestPaymentGatewayApiWithPaymentControl.java

diff --git a/payment/src/main/java/org/killbill/billing/payment/api/DefaultPaymentGatewayApi.java b/payment/src/main/java/org/killbill/billing/payment/api/DefaultPaymentGatewayApi.java
index ec17b19c60..09685fa60b 100644
--- a/payment/src/main/java/org/killbill/billing/payment/api/DefaultPaymentGatewayApi.java
+++ b/payment/src/main/java/org/killbill/billing/payment/api/DefaultPaymentGatewayApi.java
@@ -34,6 +34,7 @@ import org.killbill.billing.control.plugin.api.PriorPaymentControlResult;
 import org.killbill.billing.payment.core.PaymentExecutors;
 import org.killbill.billing.payment.core.PaymentGatewayProcessor;
 import org.killbill.billing.payment.core.sm.control.ControlPluginRunner;
+import org.killbill.billing.payment.core.sm.control.PaymentControlApiAbortException;
 import org.killbill.billing.payment.dispatcher.PluginDispatcher;
 import org.killbill.billing.payment.dispatcher.PluginDispatcher.PluginDispatcherReturnType;
 import org.killbill.billing.payment.plugin.api.GatewayNotification;
@@ -147,6 +148,8 @@ public class DefaultPaymentGatewayApi extends DefaultApiBase implements PaymentG
                                                                                                                        PaymentApiType.HPP, null, HPPType.BUILD_FORM_DESCRIPTOR,
                                                                                                                        null, null, true, paymentControlPluginNames, properties, callContext);
 
+                                                     } catch (final PaymentControlApiAbortException e) {
+                                                         throw new PaymentApiException(ErrorCode.PAYMENT_PLUGIN_API_ABORTED, e.getPluginName());
                                                      } catch (final PaymentControlApiException e) {
                                                          throw new PaymentApiException(e, ErrorCode.PAYMENT_PLUGIN_EXCEPTION, e);
                                                      }
diff --git a/payment/src/main/java/org/killbill/billing/payment/core/sm/control/ControlPluginRunner.java b/payment/src/main/java/org/killbill/billing/payment/core/sm/control/ControlPluginRunner.java
index f2b1472cc3..1ee36fc04a 100644
--- a/payment/src/main/java/org/killbill/billing/payment/core/sm/control/ControlPluginRunner.java
+++ b/payment/src/main/java/org/killbill/billing/payment/core/sm/control/ControlPluginRunner.java
@@ -25,7 +25,6 @@ import javax.annotation.Nullable;
 import javax.inject.Inject;
 
 import org.joda.time.DateTime;
-import org.killbill.billing.ErrorCode;
 import org.killbill.billing.account.api.Account;
 import org.killbill.billing.callcontext.DefaultCallContext;
 import org.killbill.billing.catalog.api.Currency;
@@ -38,7 +37,6 @@ import org.killbill.billing.control.plugin.api.PaymentControlContext;
 import org.killbill.billing.control.plugin.api.PaymentControlPluginApi;
 import org.killbill.billing.control.plugin.api.PriorPaymentControlResult;
 import org.killbill.billing.osgi.api.OSGIServiceRegistration;
-import org.killbill.billing.payment.api.PaymentApiException;
 import org.killbill.billing.payment.api.PluginProperty;
 import org.killbill.billing.payment.api.TransactionType;
 import org.killbill.billing.payment.retry.DefaultFailureCallResult;
diff --git a/payment/src/main/java/org/killbill/billing/payment/core/sm/control/PaymentControlApiAbortException.java b/payment/src/main/java/org/killbill/billing/payment/core/sm/control/PaymentControlApiAbortException.java
index c3524e5bd6..33b4df4b54 100644
--- a/payment/src/main/java/org/killbill/billing/payment/core/sm/control/PaymentControlApiAbortException.java
+++ b/payment/src/main/java/org/killbill/billing/payment/core/sm/control/PaymentControlApiAbortException.java
@@ -22,7 +22,7 @@ import org.killbill.billing.control.plugin.api.PaymentControlApiException;
 /**
  * Created by arodrigues on 5/6/16.
  */
-class PaymentControlApiAbortException extends PaymentControlApiException {
+public class PaymentControlApiAbortException extends PaymentControlApiException {
     private final String pluginName;
 
     public PaymentControlApiAbortException(final String pluginName) {
diff --git a/payment/src/test/java/org/killbill/billing/payment/api/TestPaymentGatewayApiWithPaymentControl.java b/payment/src/test/java/org/killbill/billing/payment/api/TestPaymentGatewayApiWithPaymentControl.java
index 680221b084..3aa3554519 100644
--- a/payment/src/test/java/org/killbill/billing/payment/api/TestPaymentGatewayApiWithPaymentControl.java
+++ b/payment/src/test/java/org/killbill/billing/payment/api/TestPaymentGatewayApiWithPaymentControl.java
@@ -21,6 +21,7 @@ import java.util.ArrayList;
 import java.util.List;
 import java.util.UUID;
 
+import org.killbill.billing.ErrorCode;
 import org.killbill.billing.account.api.Account;
 import org.killbill.billing.control.plugin.api.OnFailurePaymentControlResult;
 import org.killbill.billing.control.plugin.api.OnSuccessPaymentControlResult;
@@ -152,6 +153,20 @@ public class TestPaymentGatewayApiWithPaymentControl extends PaymentTestSuiteNoD
 
     }
 
+    @Test(groups = "fast")
+    public void testBuildFormDescriptorWithPaymentControlAbortedPayment() throws PaymentApiException {
+        plugin.setAborted(true);
+
+        // Set a random UUID to verify the plugin will successfully override it
+        try {
+            paymentGatewayApi.buildFormDescriptorWithPaymentControl(account, UUID.randomUUID(), ImmutableList.<PluginProperty>of(), ImmutableList.<PluginProperty>of(), paymentOptions, callContext);
+            Assert.fail();
+        } catch (PaymentApiException e) {
+            Assert.assertEquals(e.getCode(), ErrorCode.PAYMENT_PLUGIN_API_ABORTED.getCode());
+        }
+
+    }
+
     public static class TestPaymentGatewayApiValidationPlugin implements PaymentControlPluginApi {
 
         public static final String VALIDATION_PLUGIN_NAME = "TestPaymentGatewayApiValidationPlugin";
@@ -216,13 +231,20 @@ public class TestPaymentGatewayApiWithPaymentControl extends PaymentTestSuiteNoD
         private Iterable<PluginProperty> newOnResultProperties;
         private Iterable<PluginProperty> removedOnResultProperties;
 
+        private boolean aborted;
+
         public TestPaymentGatewayApiControlPlugin() {
+            this.aborted = false;
             this.newPriorCallProperties = ImmutableList.of();
             this.removedPriorCallProperties = ImmutableList.of();
             this.newOnResultProperties = ImmutableList.of();
             this.removedOnResultProperties = ImmutableList.of();
         }
 
+        public void setAborted(final boolean aborted) {
+            this.aborted = aborted;
+        }
+
         public void setPriorCallProperties(final Iterable<PluginProperty> newPriorCallProperties, final Iterable<PluginProperty> removedPriorCallProperties) {
             this.newPriorCallProperties = newPriorCallProperties;
             this.removedPriorCallProperties = removedPriorCallProperties;
@@ -235,7 +257,7 @@ public class TestPaymentGatewayApiWithPaymentControl extends PaymentTestSuiteNoD
 
         @Override
         public PriorPaymentControlResult priorCall(final PaymentControlContext paymentControlContext, final Iterable<PluginProperty> properties) throws PaymentControlApiException {
-            return new DefaultPriorPaymentControlResult(false, account.getPaymentMethodId(), null, null, getAdjustedProperties(properties, newPriorCallProperties, removedPriorCallProperties));
+            return new DefaultPriorPaymentControlResult(aborted, account.getPaymentMethodId(), null, null, getAdjustedProperties(properties, newPriorCallProperties, removedPriorCallProperties));
         }
 
         @Override