killbill-aplcache
Changes
payment/src/main/java/org/killbill/billing/payment/core/sm/control/ControlPluginRunner.java 4(+3 -1)
payment/src/main/java/org/killbill/billing/payment/core/sm/control/OperationControlCallback.java 12(+6 -6)
payment/src/main/java/org/killbill/billing/payment/core/sm/control/PaymentControlApiAbortException.java 35(+35 -0)
payment/src/test/java/org/killbill/billing/payment/provider/MockPaymentControlProviderPlugin.java 19(+19 -0)
Details
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/mappers/ExceptionMapperBase.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/mappers/ExceptionMapperBase.java
index c97bf29..95a7a37 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/mappers/ExceptionMapperBase.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/mappers/ExceptionMapperBase.java
@@ -61,7 +61,7 @@ public abstract class ExceptionMapperBase {
}
private Response doFallback(final Exception exception, final UriInfo uriInfo) {
- if (exception.getCause() == null || !(exception.getCause() instanceof BillingExceptionBase)) {
+ if (!(exception.getCause() instanceof BillingExceptionBase)) {
return buildBadRequestResponse(exception, uriInfo);
}
@@ -151,7 +151,7 @@ public abstract class ExceptionMapperBase {
return new LoggingResponse(e, responseBuilder.build());
}
- private void serializeException(final Exception e, final UriInfo uriInfo, final Response.ResponseBuilder responseBuilder) {
+ protected void serializeException(final Exception e, final UriInfo uriInfo, final Response.ResponseBuilder responseBuilder) {
final boolean withStackTrace = uriInfo.getQueryParameters() != null && "true".equals(uriInfo.getQueryParameters().getFirst(QUERY_WITH_STACK_TRACE));
final BillingExceptionJson billingExceptionJson = new BillingExceptionJson(e, withStackTrace);
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/mappers/PaymentApiExceptionMapper.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/mappers/PaymentApiExceptionMapper.java
index efa1828..799e95f 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/mappers/PaymentApiExceptionMapper.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/mappers/PaymentApiExceptionMapper.java
@@ -19,6 +19,7 @@ package org.killbill.billing.jaxrs.mappers;
import javax.inject.Singleton;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.Response;
+import javax.ws.rs.core.Response.Status;
import javax.ws.rs.core.UriInfo;
import javax.ws.rs.ext.ExceptionMapper;
import javax.ws.rs.ext.Provider;
@@ -64,14 +65,24 @@ public class PaymentApiExceptionMapper extends ExceptionMapperBase implements Ex
return buildBadRequestResponse(exception, uriInfo);
} else if (exception.getCode() == ErrorCode.PAYMENT_PLUGIN_TIMEOUT.getCode()) {
return buildPluginTimeoutResponse(exception, uriInfo);
+ } else if (exception.getCode() == ErrorCode.PAYMENT_PLUGIN_GET_PAYMENT_INFO.getCode()) {
+ return buildInternalErrorResponse(exception, uriInfo);
} else if (exception.getCode() == ErrorCode.PAYMENT_REFRESH_PAYMENT_METHOD.getCode()) {
return buildInternalErrorResponse(exception, uriInfo);
} else if (exception.getCode() == ErrorCode.PAYMENT_UPD_PAYMENT_METHOD.getCode()) {
return buildInternalErrorResponse(exception, uriInfo);
} else if (exception.getCode() == ErrorCode.PAYMENT_INVALID_PARAMETER.getCode()) {
return buildBadRequestResponse(exception, uriInfo);
+ } else if (exception.getCode() == ErrorCode.PAYMENT_PLUGIN_API_ABORTED.getCode()) {
+ return buildPaymentAbortedResponse(exception, uriInfo);
} else {
return fallback(exception, uriInfo);
}
}
+
+ private Response buildPaymentAbortedResponse(final PaymentApiException exception, final UriInfo uriInfo) {
+ final Response.ResponseBuilder responseBuilder = Response.status(422);
+ serializeException(exception, uriInfo, responseBuilder);
+ return new LoggingResponse(exception, responseBuilder.build());
+ }
}
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 e4a59f3..0a75de9 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
@@ -862,8 +862,14 @@ public class AccountResource extends JaxRsResourceBase {
@Consumes(APPLICATION_JSON)
@Produces(APPLICATION_JSON)
@ApiOperation(value = "Trigger a payment using the account external key (authorization, purchase or credit)")
- @ApiResponses(value = {@ApiResponse(code = 400, message = "Invalid account external key supplied"),
- @ApiResponse(code = 404, message = "Account not found")})
+ @ApiResponses(value = {@ApiResponse(code = 201, message = "Payment transaction created successfully"),
+ @ApiResponse(code = 400, message = "Invalid account external key supplied"),
+ @ApiResponse(code = 404, message = "Account not found"),
+ @ApiResponse(code = 402, message = "Transaction declined by gateway"),
+ @ApiResponse(code = 422, message = "Payment is aborted by a control plugin"),
+ @ApiResponse(code = 502, message = "Failed to submit payment transaction"),
+ @ApiResponse(code = 503, message = "Payment in unknown status, failed to receive gateway response"),
+ @ApiResponse(code = 504, message = "Payment operation timeout")})
public Response processPaymentByExternalKey(@MetricTag(tag = "type", property = "transactionType") final PaymentTransactionJson json,
@QueryParam(QUERY_EXTERNAL_KEY) final String externalKey,
@QueryParam(QUERY_PAYMENT_METHOD_ID) final String paymentMethodIdStr,
@@ -886,8 +892,14 @@ public class AccountResource extends JaxRsResourceBase {
@Consumes(APPLICATION_JSON)
@Produces(APPLICATION_JSON)
@ApiOperation(value = "Trigger a payment (authorization, purchase or credit)")
- @ApiResponses(value = {@ApiResponse(code = 400, message = "Invalid account id supplied"),
- @ApiResponse(code = 404, message = "Account not found")})
+ @ApiResponses(value = {@ApiResponse(code = 201, message = "Payment transaction created successfully"),
+ @ApiResponse(code = 400, message = "Invalid account id supplied"),
+ @ApiResponse(code = 404, message = "Account not found"),
+ @ApiResponse(code = 402, message = "Transaction declined by gateway"),
+ @ApiResponse(code = 422, message = "Payment is aborted by a control plugin"),
+ @ApiResponse(code = 502, message = "Failed to submit payment transaction"),
+ @ApiResponse(code = 503, message = "Payment in unknown status, failed to receive gateway response"),
+ @ApiResponse(code = 504, message = "Payment operation timeout")})
public Response processPayment(@MetricTag(tag = "type", property = "transactionType") final PaymentTransactionJson json,
@PathParam(QUERY_ACCOUNT_ID) final String accountIdStr,
@QueryParam(QUERY_PAYMENT_METHOD_ID) final String paymentMethodIdStr,
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 82db576..bf820e2 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
@@ -232,8 +232,14 @@ public class PaymentResource extends ComboPaymentResource {
@Consumes(APPLICATION_JSON)
@Produces(APPLICATION_JSON)
@ApiOperation(value = "Complete an existing transaction")
- @ApiResponses(value = {@ApiResponse(code = 400, message = "Invalid paymentId supplied"),
- @ApiResponse(code = 404, message = "Account or payment not found")})
+ @ApiResponses(value = {@ApiResponse(code = 201, message = "Payment transaction created successfully"),
+ @ApiResponse(code = 400, message = "Invalid paymentId supplied"),
+ @ApiResponse(code = 404, message = "Account or payment not found"),
+ @ApiResponse(code = 402, message = "Transaction declined by gateway"),
+ @ApiResponse(code = 422, message = "Payment is aborted by a control plugin"),
+ @ApiResponse(code = 502, message = "Failed to submit payment transaction"),
+ @ApiResponse(code = 503, message = "Payment in unknown status, failed to receive gateway response"),
+ @ApiResponse(code = 504, message = "Payment operation timeout")})
public Response completeTransaction(@MetricTag(tag = "type", property = "transactionType") final PaymentTransactionJson json,
@PathParam("paymentId") final String paymentIdStr,
@QueryParam(QUERY_PAYMENT_CONTROL_PLUGIN_NAME) final List<String> paymentControlPluginNames,
@@ -251,7 +257,13 @@ public class PaymentResource extends ComboPaymentResource {
@Consumes(APPLICATION_JSON)
@Produces(APPLICATION_JSON)
@ApiOperation(value = "Complete an existing transaction")
- @ApiResponses(value = {@ApiResponse(code = 404, message = "Account or payment not found")})
+ @ApiResponses(value = {@ApiResponse(code = 201, message = "Payment transaction created successfully"),
+ @ApiResponse(code = 404, message = "Account or payment not found"),
+ @ApiResponse(code = 402, message = "Transaction declined by gateway"),
+ @ApiResponse(code = 422, message = "Payment is aborted by a control plugin"),
+ @ApiResponse(code = 502, message = "Failed to submit payment transaction"),
+ @ApiResponse(code = 503, message = "Payment in unknown status, failed to receive gateway response"),
+ @ApiResponse(code = 504, message = "Payment operation timeout")})
public Response completeTransactionByExternalKey(@MetricTag(tag = "type", property = "transactionType") final PaymentTransactionJson json,
@QueryParam(QUERY_PAYMENT_CONTROL_PLUGIN_NAME) final List<String> paymentControlPluginNames,
@QueryParam(QUERY_PLUGIN_PROPERTY) final List<String> pluginPropertiesString,
@@ -329,8 +341,14 @@ public class PaymentResource extends ComboPaymentResource {
@Consumes(APPLICATION_JSON)
@Produces(APPLICATION_JSON)
@ApiOperation(value = "Capture an existing authorization")
- @ApiResponses(value = {@ApiResponse(code = 400, message = "Invalid paymentId supplied"),
- @ApiResponse(code = 404, message = "Account or payment not found")})
+ @ApiResponses(value = {@ApiResponse(code = 201, message = "Payment transaction created successfully"),
+ @ApiResponse(code = 400, message = "Invalid paymentId supplied"),
+ @ApiResponse(code = 404, message = "Account or payment not found"),
+ @ApiResponse(code = 402, message = "Transaction declined by gateway"),
+ @ApiResponse(code = 422, message = "Payment is aborted by a control plugin"),
+ @ApiResponse(code = 502, message = "Failed to submit payment transaction"),
+ @ApiResponse(code = 503, message = "Payment in unknown status, failed to receive gateway response"),
+ @ApiResponse(code = 504, message = "Payment operation timeout")})
public Response captureAuthorization(final PaymentTransactionJson json,
@PathParam("paymentId") final String paymentIdStr,
@QueryParam(QUERY_PAYMENT_CONTROL_PLUGIN_NAME) final List<String> paymentControlPluginNames,
@@ -348,7 +366,13 @@ public class PaymentResource extends ComboPaymentResource {
@Consumes(APPLICATION_JSON)
@Produces(APPLICATION_JSON)
@ApiOperation(value = "Capture an existing authorization")
- @ApiResponses(value = {@ApiResponse(code = 404, message = "Account or payment not found")})
+ @ApiResponses(value = {@ApiResponse(code = 201, message = "Payment transaction created successfully"),
+ @ApiResponse(code = 404, message = "Account or payment not found"),
+ @ApiResponse(code = 402, message = "Transaction declined by gateway"),
+ @ApiResponse(code = 422, message = "Payment is aborted by a control plugin"),
+ @ApiResponse(code = 502, message = "Failed to submit payment transaction"),
+ @ApiResponse(code = 503, message = "Payment in unknown status, failed to receive gateway response"),
+ @ApiResponse(code = 504, message = "Payment operation timeout")})
public Response captureAuthorizationByExternalKey(final PaymentTransactionJson json,
@QueryParam(QUERY_PAYMENT_CONTROL_PLUGIN_NAME) final List<String> paymentControlPluginNames,
@QueryParam(QUERY_PLUGIN_PROPERTY) final List<String> pluginPropertiesString,
@@ -393,8 +417,14 @@ public class PaymentResource extends ComboPaymentResource {
@Consumes(APPLICATION_JSON)
@Produces(APPLICATION_JSON)
@ApiOperation(value = "Refund an existing payment")
- @ApiResponses(value = {@ApiResponse(code = 400, message = "Invalid paymentId supplied"),
- @ApiResponse(code = 404, message = "Account or payment not found")})
+ @ApiResponses(value = {@ApiResponse(code = 201, message = "Payment transaction created successfully"),
+ @ApiResponse(code = 400, message = "Invalid paymentId supplied"),
+ @ApiResponse(code = 404, message = "Account or payment not found"),
+ @ApiResponse(code = 402, message = "Transaction declined by gateway"),
+ @ApiResponse(code = 422, message = "Payment is aborted by a control plugin"),
+ @ApiResponse(code = 502, message = "Failed to submit payment transaction"),
+ @ApiResponse(code = 503, message = "Payment in unknown status, failed to receive gateway response"),
+ @ApiResponse(code = 504, message = "Payment operation timeout")})
public Response refundPayment(final PaymentTransactionJson json,
@PathParam("paymentId") final String paymentIdStr,
@QueryParam(QUERY_PAYMENT_CONTROL_PLUGIN_NAME) final List<String> paymentControlPluginNames,
@@ -413,7 +443,13 @@ public class PaymentResource extends ComboPaymentResource {
@Consumes(APPLICATION_JSON)
@Produces(APPLICATION_JSON)
@ApiOperation(value = "Refund an existing payment")
- @ApiResponses(value = {@ApiResponse(code = 404, message = "Account or payment not found")})
+ @ApiResponses(value = {@ApiResponse(code = 201, message = "Payment transaction created successfully"),
+ @ApiResponse(code = 404, message = "Account or payment not found"),
+ @ApiResponse(code = 402, message = "Transaction declined by gateway"),
+ @ApiResponse(code = 422, message = "Payment is aborted by a control plugin"),
+ @ApiResponse(code = 502, message = "Failed to submit payment transaction"),
+ @ApiResponse(code = 503, message = "Payment in unknown status, failed to receive gateway response"),
+ @ApiResponse(code = 504, message = "Payment operation timeout")})
public Response refundPaymentByExternalKey(final PaymentTransactionJson json,
@QueryParam(QUERY_PAYMENT_CONTROL_PLUGIN_NAME) final List<String> paymentControlPluginNames,
@QueryParam(QUERY_PLUGIN_PROPERTY) final List<String> pluginPropertiesString,
@@ -460,8 +496,14 @@ public class PaymentResource extends ComboPaymentResource {
@Consumes(APPLICATION_JSON)
@Produces(APPLICATION_JSON)
@ApiOperation(value = "Void an existing payment")
- @ApiResponses(value = {@ApiResponse(code = 400, message = "Invalid paymentId supplied"),
- @ApiResponse(code = 404, message = "Account or payment not found")})
+ @ApiResponses(value = {@ApiResponse(code = 201, message = "Payment transaction created successfully"),
+ @ApiResponse(code = 400, message = "Invalid paymentId supplied"),
+ @ApiResponse(code = 404, message = "Account or payment not found"),
+ @ApiResponse(code = 402, message = "Transaction declined by gateway"),
+ @ApiResponse(code = 422, message = "Payment is aborted by a control plugin"),
+ @ApiResponse(code = 502, message = "Failed to submit payment transaction"),
+ @ApiResponse(code = 503, message = "Payment in unknown status, failed to receive gateway response"),
+ @ApiResponse(code = 504, message = "Payment operation timeout")})
public Response voidPayment(final PaymentTransactionJson json,
@PathParam("paymentId") final String paymentIdStr,
@QueryParam(QUERY_PAYMENT_CONTROL_PLUGIN_NAME) final List<String> paymentControlPluginNames,
@@ -479,7 +521,13 @@ public class PaymentResource extends ComboPaymentResource {
@Consumes(APPLICATION_JSON)
@Produces(APPLICATION_JSON)
@ApiOperation(value = "Void an existing payment")
- @ApiResponses(value = {@ApiResponse(code = 404, message = "Account or payment not found")})
+ @ApiResponses(value = {@ApiResponse(code = 201, message = "Payment transaction created successfully"),
+ @ApiResponse(code = 404, message = "Account or payment not found"),
+ @ApiResponse(code = 402, message = "Transaction declined by gateway"),
+ @ApiResponse(code = 422, message = "Payment is aborted by a control plugin"),
+ @ApiResponse(code = 502, message = "Failed to submit payment transaction"),
+ @ApiResponse(code = 503, message = "Payment in unknown status, failed to receive gateway response"),
+ @ApiResponse(code = 504, message = "Payment operation timeout")})
public Response voidPaymentByExternalKey(final PaymentTransactionJson json,
@QueryParam(QUERY_PAYMENT_CONTROL_PLUGIN_NAME) final List<String> paymentControlPluginNames,
@QueryParam(QUERY_PLUGIN_PROPERTY) final List<String> pluginPropertiesString,
@@ -520,8 +568,14 @@ public class PaymentResource extends ComboPaymentResource {
@Consumes(APPLICATION_JSON)
@Produces(APPLICATION_JSON)
@ApiOperation(value = "Record a chargeback")
- @ApiResponses(value = {@ApiResponse(code = 400, message = "Invalid paymentId supplied"),
- @ApiResponse(code = 404, message = "Account not found")})
+ @ApiResponses(value = {@ApiResponse(code = 201, message = "Payment transaction created successfully"),
+ @ApiResponse(code = 400, message = "Invalid paymentId supplied"),
+ @ApiResponse(code = 404, message = "Account or payment not found"),
+ @ApiResponse(code = 402, message = "Transaction declined by gateway"),
+ @ApiResponse(code = 422, message = "Payment is aborted by a control plugin"),
+ @ApiResponse(code = 502, message = "Failed to submit payment transaction"),
+ @ApiResponse(code = 503, message = "Payment in unknown status, failed to receive gateway response"),
+ @ApiResponse(code = 504, message = "Payment operation timeout")})
public Response chargebackPayment(final PaymentTransactionJson json,
@PathParam("paymentId") final String paymentIdStr,
@QueryParam(QUERY_PAYMENT_CONTROL_PLUGIN_NAME) final List<String> paymentControlPluginNames,
@@ -540,7 +594,13 @@ public class PaymentResource extends ComboPaymentResource {
@Consumes(APPLICATION_JSON)
@Produces(APPLICATION_JSON)
@ApiOperation(value = "Record a chargeback")
- @ApiResponses(value = {@ApiResponse(code = 404, message = "Account not found")})
+ @ApiResponses(value = {@ApiResponse(code = 201, message = "Payment transaction created successfully"),
+ @ApiResponse(code = 404, message = "Account or payment not found"),
+ @ApiResponse(code = 402, message = "Transaction declined by gateway"),
+ @ApiResponse(code = 422, message = "Payment is aborted by a control plugin"),
+ @ApiResponse(code = 502, message = "Failed to submit payment transaction"),
+ @ApiResponse(code = 503, message = "Payment in unknown status, failed to receive gateway response"),
+ @ApiResponse(code = 504, message = "Payment operation timeout")})
public Response chargebackPaymentByExternalKey(final PaymentTransactionJson json,
@QueryParam(QUERY_PAYMENT_CONTROL_PLUGIN_NAME) final List<String> paymentControlPluginNames,
@QueryParam(QUERY_PLUGIN_PROPERTY) final List<String> pluginPropertiesString,
@@ -584,7 +644,13 @@ public class PaymentResource extends ComboPaymentResource {
@Produces(APPLICATION_JSON)
@Path("/" + COMBO)
@ApiOperation(value = "Combo api to create a new payment transaction on a existing (or not) account ")
- @ApiResponses(value = {@ApiResponse(code = 400, message = "Invalid data for Account or PaymentMethod")})
+ @ApiResponses(value = {@ApiResponse(code = 201, message = "Payment transaction created successfully"),
+ @ApiResponse(code = 400, message = "Invalid data for Account or PaymentMethod"),
+ @ApiResponse(code = 402, message = "Transaction declined by gateway"),
+ @ApiResponse(code = 422, message = "Payment is aborted by a control plugin"),
+ @ApiResponse(code = 502, message = "Failed to submit payment transaction"),
+ @ApiResponse(code = 503, message = "Payment in unknown status, failed to receive gateway response"),
+ @ApiResponse(code = 504, message = "Payment operation timeout")})
public Response createComboPayment(@MetricTag(tag = "type", property = "transactionType") final ComboPaymentTransactionJson json,
@QueryParam(QUERY_PAYMENT_CONTROL_PLUGIN_NAME) final List<String> paymentControlPluginNames,
@HeaderParam(HDR_CREATED_BY) final String createdBy,
diff --git a/payment/src/main/java/org/killbill/billing/payment/core/PaymentProcessor.java b/payment/src/main/java/org/killbill/billing/payment/core/PaymentProcessor.java
index 7aa74e5..1a92573 100644
--- a/payment/src/main/java/org/killbill/billing/payment/core/PaymentProcessor.java
+++ b/payment/src/main/java/org/killbill/billing/payment/core/PaymentProcessor.java
@@ -364,7 +364,7 @@ public class PaymentProcessor extends ProcessorBase {
try {
return plugin.getPaymentInfo(paymentModelDao.getAccountId(), paymentModelDao.getId(), properties, context);
} catch (final PaymentPluginApiException e) {
- throw new PaymentApiException(ErrorCode.PAYMENT_PLUGIN_GET_PAYMENT_INFO, paymentModelDao.getId(), e.toString());
+ throw new PaymentApiException(e, ErrorCode.PAYMENT_PLUGIN_GET_PAYMENT_INFO, paymentModelDao.getId(), e.toString());
}
}
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 7f0ba27..f2b1472 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,6 +25,7 @@ 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;
@@ -37,6 +38,7 @@ 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;
@@ -117,7 +119,7 @@ public class ControlPluginRunner {
inputPluginProperties = prevResult.getAdjustedPluginProperties();
}
if (prevResult.isAborted()) {
- break;
+ throw new PaymentControlApiAbortException(pluginName);
}
inputPaymentControlContext = new DefaultPaymentControlContext(account,
inputPaymentMethodId,
diff --git a/payment/src/main/java/org/killbill/billing/payment/core/sm/control/OperationControlCallback.java b/payment/src/main/java/org/killbill/billing/payment/core/sm/control/OperationControlCallback.java
index 8c64a2d..13ad2dc 100644
--- a/payment/src/main/java/org/killbill/billing/payment/core/sm/control/OperationControlCallback.java
+++ b/payment/src/main/java/org/killbill/billing/payment/core/sm/control/OperationControlCallback.java
@@ -25,6 +25,7 @@ import org.joda.time.DateTime;
import org.killbill.automaton.Operation.OperationCallback;
import org.killbill.automaton.OperationException;
import org.killbill.automaton.OperationResult;
+import org.killbill.billing.ErrorCode;
import org.killbill.billing.control.plugin.api.OnFailurePaymentControlResult;
import org.killbill.billing.control.plugin.api.OnSuccessPaymentControlResult;
import org.killbill.billing.control.plugin.api.PaymentApiType;
@@ -99,13 +100,12 @@ public abstract class OperationControlCallback extends OperationCallbackBase<Pay
paymentStateControlContext.isApiPayment(),
paymentStateContext.getCallContext());
- final PriorPaymentControlResult pluginResult;
try {
- pluginResult = executePluginPriorCalls(paymentStateControlContext.getPaymentControlPluginNames(), paymentControlContext);
- if (pluginResult != null && pluginResult.isAborted()) {
- // Transition to ABORTED
- return PluginDispatcher.createPluginDispatcherReturnType(OperationResult.EXCEPTION);
- }
+ executePluginPriorCalls(paymentStateControlContext.getPaymentControlPluginNames(), paymentControlContext);
+ } catch (final PaymentControlApiAbortException e) {
+ // Transition to ABORTED
+ final PaymentApiException paymentAbortedException = new PaymentApiException(ErrorCode.PAYMENT_PLUGIN_API_ABORTED, e.getPluginName());
+ throw new OperationException(paymentAbortedException, OperationResult.EXCEPTION);
} catch (final PaymentControlApiException e) {
// Transition to ABORTED and throw PaymentControlApiException to caller.
throw new OperationException(e, OperationResult.EXCEPTION);
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
new file mode 100644
index 0000000..c3524e5
--- /dev/null
+++ b/payment/src/main/java/org/killbill/billing/payment/core/sm/control/PaymentControlApiAbortException.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 The Billing Project, LLC
+ *
+ * The Billing Project licenses this file to you under the Apache License, version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * 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.core.sm.control;
+
+import org.killbill.billing.control.plugin.api.PaymentControlApiException;
+
+/**
+ * Created by arodrigues on 5/6/16.
+ */
+class PaymentControlApiAbortException extends PaymentControlApiException {
+ private final String pluginName;
+
+ public PaymentControlApiAbortException(final String pluginName) {
+ this.pluginName = pluginName;
+ }
+
+ public String getPluginName() {
+ return pluginName;
+ }
+}
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 aadbcea..ac5ec07 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
@@ -320,6 +320,23 @@ public class TestPaymentApi extends PaymentTestSuiteWithEmbeddedDB {
@Test(groups = "slow")
public void testCreatePurchaseWithControlPluginException() throws Exception {
+ mockPaymentControlProviderPlugin.throwsException(new PaymentControlApiException());
+
+ final BigDecimal requestedAmount = BigDecimal.TEN;
+ final String paymentExternalKey = "pay controle external key";;
+ final String transactionExternalKey = "txn control external key";
+ try {
+ paymentApi.createPurchaseWithPaymentControl(
+ account, account.getPaymentMethodId(), null, requestedAmount, Currency.AED,
+ paymentExternalKey, transactionExternalKey, ImmutableList.<PluginProperty>of(), CONTROL_PLUGIN_OPTIONS, callContext);
+ fail();
+ } catch (PaymentApiException e) {
+ assertTrue(e.getCause() instanceof PaymentControlApiException);
+ }
+ }
+
+ @Test(groups = "slow")
+ public void testCreatePurchaseWithControlPluginRuntimeException() throws Exception {
mockPaymentControlProviderPlugin.throwsException(new IllegalStateException());
final BigDecimal requestedAmount = BigDecimal.TEN;
diff --git a/payment/src/test/java/org/killbill/billing/payment/core/sm/TestRetryablePayment.java b/payment/src/test/java/org/killbill/billing/payment/core/sm/TestRetryablePayment.java
index 5a50314..9f6f8d8 100644
--- a/payment/src/test/java/org/killbill/billing/payment/core/sm/TestRetryablePayment.java
+++ b/payment/src/test/java/org/killbill/billing/payment/core/sm/TestRetryablePayment.java
@@ -73,7 +73,9 @@ import com.google.inject.Inject;
import static org.killbill.billing.payment.glue.PaymentModule.RETRYABLE_NAMED;
import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertFalse;
import static org.testng.Assert.assertNotNull;
+import static org.testng.Assert.fail;
public class TestRetryablePayment extends PaymentTestSuiteNoDB {
@@ -227,19 +229,26 @@ public class TestRetryablePayment extends PaymentTestSuiteNoDB {
runner.setOperationCallback(mockRetryAuthorizeOperationCallback)
.setContext(paymentStateContext);
- runner.run(true,
- TransactionType.AUTHORIZE,
- account,
- paymentMethodId,
- null,
- paymentExternalKey,
- paymentTransactionExternalKey,
- amount,
- currency,
- emptyProperties,
- null,
- callContext,
- internalCallContext);
+ try {
+ runner.run(true,
+ TransactionType.AUTHORIZE,
+ account,
+ paymentMethodId,
+ null,
+ paymentExternalKey,
+ paymentTransactionExternalKey,
+ amount,
+ currency,
+ emptyProperties,
+ null,
+ callContext,
+ internalCallContext);
+ fail();
+ } catch (PaymentApiException e) {
+ assertEquals(e.getCode(), ErrorCode.PAYMENT_PLUGIN_API_ABORTED.getCode());
+ }
+ assertFalse(mockRetryProviderPlugin.isOnSuccessCallExecuted(), "OnSuccessCall method should not be called when payment is aborted");
+ assertFalse(mockRetryProviderPlugin.isOnFailureCallExecuted(), "onFailureCall method should not be called when payment is aborted");
final PaymentAttemptModelDao pa = paymentDao.getPaymentAttemptByTransactionExternalKey(paymentTransactionExternalKey, internalCallContext).get(0);
assertEquals(pa.getTransactionExternalKey(), paymentTransactionExternalKey);
@@ -342,7 +351,7 @@ public class TestRetryablePayment extends PaymentTestSuiteNoDB {
null,
callContext, internalCallContext);
- Assert.fail("Expected PaymentApiException...");
+ fail("Expected PaymentApiException...");
} catch (final PaymentApiException e) {
final PaymentAttemptModelDao pa = paymentDao.getPaymentAttemptByTransactionExternalKey(paymentTransactionExternalKey, internalCallContext).get(0);
@@ -380,7 +389,7 @@ public class TestRetryablePayment extends PaymentTestSuiteNoDB {
null,
callContext, internalCallContext);
- Assert.fail("Expected PaymentApiException...");
+ fail("Expected PaymentApiException...");
} catch (final PaymentApiException e) {
final PaymentAttemptModelDao pa = paymentDao.getPaymentAttemptByTransactionExternalKey(paymentTransactionExternalKey, internalCallContext).get(0);
assertEquals(pa.getTransactionExternalKey(), paymentTransactionExternalKey);
@@ -417,7 +426,7 @@ public class TestRetryablePayment extends PaymentTestSuiteNoDB {
null,
callContext, internalCallContext);
- Assert.fail("Expected Exception...");
+ fail("Expected Exception...");
} catch (final PaymentApiException e) {
final PaymentAttemptModelDao pa = paymentDao.getPaymentAttemptByTransactionExternalKey(paymentTransactionExternalKey, internalCallContext).get(0);
assertEquals(pa.getTransactionExternalKey(), paymentTransactionExternalKey);
@@ -454,7 +463,7 @@ public class TestRetryablePayment extends PaymentTestSuiteNoDB {
null,
callContext, internalCallContext);
- Assert.fail("Expected Exception...");
+ fail("Expected Exception...");
} catch (final PaymentApiException e) {
final PaymentAttemptModelDao pa = paymentDao.getPaymentAttemptByTransactionExternalKey(paymentTransactionExternalKey, internalCallContext).get(0);
assertEquals(pa.getTransactionExternalKey(), paymentTransactionExternalKey);
@@ -549,7 +558,7 @@ public class TestRetryablePayment extends PaymentTestSuiteNoDB {
callContext,
internalCallContext);
- Assert.fail("Expecting paymentApiException...");
+ fail("Expecting paymentApiException...");
} catch (final PaymentApiException e) {
final PaymentAttemptModelDao pa = paymentDao.getPaymentAttemptByTransactionExternalKey(paymentTransactionExternalKey, internalCallContext).get(0);
assertEquals(pa.getTransactionExternalKey(), paymentTransactionExternalKey);
@@ -596,7 +605,7 @@ public class TestRetryablePayment extends PaymentTestSuiteNoDB {
callContext,
internalCallContext);
- Assert.fail("Expecting paymentApiException...");
+ fail("Expecting paymentApiException...");
} catch (final PaymentApiException e) {
final List<PaymentAttemptModelDao> pas = paymentDao.getPaymentAttemptByTransactionExternalKey(paymentTransactionExternalKey, internalCallContext);
diff --git a/payment/src/test/java/org/killbill/billing/payment/provider/MockPaymentControlProviderPlugin.java b/payment/src/test/java/org/killbill/billing/payment/provider/MockPaymentControlProviderPlugin.java
index c150594..3719daf 100644
--- a/payment/src/test/java/org/killbill/billing/payment/provider/MockPaymentControlProviderPlugin.java
+++ b/payment/src/test/java/org/killbill/billing/payment/provider/MockPaymentControlProviderPlugin.java
@@ -37,6 +37,10 @@ public class MockPaymentControlProviderPlugin implements PaymentControlPluginApi
private DateTime nextRetryDate;
private Exception exception;
+ private boolean priorCallExecuted;
+ private boolean onSuccessCallExecuted;
+ private boolean onFailureCallExecuted;
+
public MockPaymentControlProviderPlugin setAborted(final boolean isAborted) {
this.isAborted = isAborted;
return this;
@@ -59,6 +63,7 @@ public class MockPaymentControlProviderPlugin implements PaymentControlPluginApi
@Override
public PriorPaymentControlResult priorCall(final PaymentControlContext paymentControlContext, final Iterable<PluginProperty> properties) throws PaymentControlApiException {
+ priorCallExecuted = true;
if (exception instanceof PaymentControlApiException) {
throw (PaymentControlApiException) exception;
} else if (exception instanceof RuntimeException) {
@@ -69,11 +74,25 @@ public class MockPaymentControlProviderPlugin implements PaymentControlPluginApi
@Override
public OnSuccessPaymentControlResult onSuccessCall(final PaymentControlContext paymentControlContext, final Iterable<PluginProperty> properties) throws PaymentControlApiException {
+ onSuccessCallExecuted = true;
return new DefaultOnSuccessPaymentControlResult();
}
@Override
public OnFailurePaymentControlResult onFailureCall(final PaymentControlContext paymentControlContext, final Iterable<PluginProperty> properties) throws PaymentControlApiException {
+ onFailureCallExecuted = true;
return new DefaultFailureCallResult(nextRetryDate);
}
+
+ public boolean isPriorCallExecuted() {
+ return priorCallExecuted;
+ }
+
+ public boolean isOnSuccessCallExecuted() {
+ return onSuccessCallExecuted;
+ }
+
+ public boolean isOnFailureCallExecuted() {
+ return onFailureCallExecuted;
+ }
}
diff --git a/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestPayment.java b/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestPayment.java
index 20478a2..69a716d 100644
--- a/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestPayment.java
+++ b/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestPayment.java
@@ -52,6 +52,7 @@ import com.google.common.collect.ImmutableMap;
import com.google.inject.Inject;
import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertFalse;
import static org.testng.Assert.fail;
public class TestPayment extends TestJaxrsBase {
@@ -442,6 +443,24 @@ public class TestPayment extends TestJaxrsBase {
}
@Test(groups = "slow")
+ public void testComboAuthorizationAbortedPayment() throws Exception {
+ final Account accountJson = getAccount();
+ accountJson.setAccountId(null);
+ final String paymentExternalKey = UUID.randomUUID().toString();
+ final ComboPaymentTransaction comboPaymentTransaction = createComboPaymentTransaction(accountJson, paymentExternalKey);
+
+ mockPaymentControlProviderPlugin.setAborted(true);
+ try {
+ killBillClient.createPayment(comboPaymentTransaction, Arrays.asList(MockPaymentControlProviderPlugin.PLUGIN_NAME), ImmutableMap.<String, String>of(), createdBy, reason, comment);
+ fail();
+ } catch (KillBillClientException e) {
+ assertEquals(e.getResponse().getStatusCode(), 422);
+ }
+ assertFalse(mockPaymentControlProviderPlugin.isOnFailureCallExecuted());
+ assertFalse(mockPaymentControlProviderPlugin.isOnSuccessCallExecuted());
+ }
+
+ @Test(groups = "slow")
public void testComboAuthorizationControlPluginException() throws Exception {
final Account accountJson = getAccount();
accountJson.setAccountId(null);
diff --git a/util/src/main/java/org/killbill/billing/util/security/shiro/dao/JDBCSessionDao.java b/util/src/main/java/org/killbill/billing/util/security/shiro/dao/JDBCSessionDao.java
index 0b0188b..ac29706 100644
--- a/util/src/main/java/org/killbill/billing/util/security/shiro/dao/JDBCSessionDao.java
+++ b/util/src/main/java/org/killbill/billing/util/security/shiro/dao/JDBCSessionDao.java
@@ -1,7 +1,7 @@
/*
* Copyright 2010-2013 Ning, Inc.
- * Copyright 2014-2015 Groupon, Inc
- * Copyright 2014-2015 The Billing Project, LLC
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 The Billing Project, LLC
*
* The Billing Project licenses this file to you under the Apache License, version 2.0
* (the "License"); you may not use this file except in compliance with the
@@ -25,8 +25,6 @@ import javax.inject.Inject;
import org.apache.shiro.session.Session;
import org.apache.shiro.session.mgt.eis.CachingSessionDAO;
-import org.joda.time.DateTime;
-import org.joda.time.DateTimeZone;
import org.skife.jdbi.v2.IDBI;
import org.skife.jdbi.v2.Transaction;
import org.skife.jdbi.v2.TransactionStatus;
@@ -46,10 +44,7 @@ public class JDBCSessionDao extends CachingSessionDAO {
@Override
protected void doUpdate(final Session session) {
- // Assume only the last access time attribute was updated (see https://github.com/killbill/killbill/issues/326)
- final DateTime lastAccessTime = new DateTime(session.getLastAccessTime(), DateTimeZone.UTC);
- final Long sessionId = Long.valueOf(session.getId().toString());
- jdbcSessionSqlDao.updateLastAccessTime(lastAccessTime, sessionId);
+ jdbcSessionSqlDao.update(new SessionModelDao(session));
}
@Override
diff --git a/util/src/main/java/org/killbill/billing/util/security/shiro/dao/JDBCSessionSqlDao.java b/util/src/main/java/org/killbill/billing/util/security/shiro/dao/JDBCSessionSqlDao.java
index 68000d9..f8d9322 100644
--- a/util/src/main/java/org/killbill/billing/util/security/shiro/dao/JDBCSessionSqlDao.java
+++ b/util/src/main/java/org/killbill/billing/util/security/shiro/dao/JDBCSessionSqlDao.java
@@ -1,7 +1,7 @@
/*
* Copyright 2010-2013 Ning, Inc.
- * Copyright 2014-2015 Groupon, Inc
- * Copyright 2014-2015 The Billing Project, LLC
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 The Billing Project, LLC
*
* The Billing Project licenses this file to you under the Apache License, version 2.0
* (the "License"); you may not use this file except in compliance with the
@@ -18,7 +18,6 @@
package org.killbill.billing.util.security.shiro.dao;
-import org.joda.time.DateTime;
import org.killbill.billing.util.entity.dao.EntitySqlDaoStringTemplate;
import org.killbill.commons.jdbi.binder.SmartBindBean;
import org.skife.jdbi.v2.sqlobject.Bind;
@@ -39,9 +38,6 @@ public interface JDBCSessionSqlDao extends Transactional<JDBCSessionSqlDao> {
public void update(@SmartBindBean final SessionModelDao sessionModelDao);
@SqlUpdate
- public void updateLastAccessTime(@Bind("lastAccessTime") final DateTime lastAccessTime, @Bind("recordId") final Long sessionId);
-
- @SqlUpdate
public void delete(@SmartBindBean final SessionModelDao sessionModelDao);
@SqlQuery
diff --git a/util/src/main/resources/org/killbill/billing/util/security/shiro/dao/JDBCSessionSqlDao.sql.stg b/util/src/main/resources/org/killbill/billing/util/security/shiro/dao/JDBCSessionSqlDao.sql.stg
index 2599c29..fe228d2 100644
--- a/util/src/main/resources/org/killbill/billing/util/security/shiro/dao/JDBCSessionSqlDao.sql.stg
+++ b/util/src/main/resources/org/killbill/billing/util/security/shiro/dao/JDBCSessionSqlDao.sql.stg
@@ -40,13 +40,6 @@ where record_id = :recordId
;
>>
-updateLastAccessTime() ::= <<
-update sessions set
- last_access_time = :lastAccessTime
-where record_id = :recordId
-;
->>
-
delete() ::= <<
delete from sessions
where record_id = :recordId
diff --git a/util/src/test/java/org/killbill/billing/util/security/shiro/dao/TestJDBCSessionDao.java b/util/src/test/java/org/killbill/billing/util/security/shiro/dao/TestJDBCSessionDao.java
index df97ca8..c8da341 100644
--- a/util/src/test/java/org/killbill/billing/util/security/shiro/dao/TestJDBCSessionDao.java
+++ b/util/src/test/java/org/killbill/billing/util/security/shiro/dao/TestJDBCSessionDao.java
@@ -22,7 +22,6 @@ import java.util.UUID;
import org.apache.shiro.session.Session;
import org.apache.shiro.session.mgt.SimpleSession;
-import org.joda.time.DateTime;
import org.killbill.billing.util.UtilTestSuiteWithEmbeddedDB;
import org.testng.Assert;
import org.testng.annotations.Test;
@@ -57,11 +56,11 @@ public class TestJDBCSessionDao extends UtilTestSuiteWithEmbeddedDB {
Assert.assertEquals(retrievedSession, session);
// Update
- final Date lastAccessTime = DateTime.now().withTimeAtStartOfDay().toDate(); // Milliseconds will be truncated
- Assert.assertNotEquals(retrievedSession.getLastAccessTime(), lastAccessTime);
- session.setLastAccessTime(lastAccessTime);
+ final String newHost = UUID.randomUUID().toString();
+ Assert.assertNotEquals(retrievedSession.getHost(), newHost);
+ session.setHost(newHost);
jdbcSessionDao.doUpdate(session);
- Assert.assertEquals(jdbcSessionDao.doReadSession(sessionId).getLastAccessTime().compareTo(lastAccessTime), 0);
+ Assert.assertEquals(jdbcSessionDao.doReadSession(sessionId).getHost(), newHost);
// Delete
jdbcSessionDao.doDelete(session);