Details
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/AdminPaymentJson.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/AdminPaymentJson.java
new file mode 100644
index 0000000..3ed676e
--- /dev/null
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/AdminPaymentJson.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2014-2015 Groupon, Inc
+ * Copyright 2014-2015 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.jaxrs.json;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+public class AdminPaymentJson {
+
+ private final String lastSuccessPaymentState;
+ private final String currentPaymentStateName;
+ private final String transactionStatus;
+
+ @JsonCreator
+ public AdminPaymentJson(@JsonProperty("lastSuccessPaymentState") final String lastSuccessPaymentState,
+ @JsonProperty("currentPaymentStateName") final String currentPaymentStateName,
+ @JsonProperty("transactionStatus") final String transactionStatus) {
+ this.lastSuccessPaymentState = lastSuccessPaymentState;
+ this.currentPaymentStateName = currentPaymentStateName;
+ this.transactionStatus = transactionStatus;
+ }
+
+ public String getLastSuccessPaymentState() {
+ return lastSuccessPaymentState;
+ }
+
+ public String getCurrentPaymentStateName() {
+ return currentPaymentStateName;
+ }
+
+ public String getTransactionStatus() {
+ return transactionStatus;
+ }
+
+ @Override
+ public String toString() {
+ return "AdminPaymentJson{" +
+ "lastSuccessPaymentState='" + lastSuccessPaymentState + '\'' +
+ ", currentPaymentStateName='" + currentPaymentStateName + '\'' +
+ ", transactionStatus='" + transactionStatus + '\'' +
+ '}';
+ }
+}
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
new file mode 100644
index 0000000..ed98edb
--- /dev/null
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/AdminResource.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright 2014-2015 Groupon, Inc
+ * Copyright 2014-2015 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.jaxrs.resources;
+
+import java.util.UUID;
+
+import javax.inject.Inject;
+import javax.servlet.http.HttpServletRequest;
+import javax.ws.rs.Consumes;
+import javax.ws.rs.HeaderParam;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.Response.Status;
+
+import org.killbill.billing.account.api.AccountUserApi;
+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.Payment;
+import org.killbill.billing.payment.api.PaymentApi;
+import org.killbill.billing.payment.api.PaymentApiException;
+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.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.clock.Clock;
+
+import com.google.common.base.Predicate;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
+import com.google.inject.Singleton;
+import com.wordnik.swagger.annotations.Api;
+import com.wordnik.swagger.annotations.ApiOperation;
+import com.wordnik.swagger.annotations.ApiResponse;
+import com.wordnik.swagger.annotations.ApiResponses;
+
+import static javax.ws.rs.core.MediaType.APPLICATION_JSON;
+
+@Singleton
+@Path(JaxrsResource.ADMIN_PATH)
+@Api(value = JaxrsResource.ADMIN_PATH, description = "Admin operations (will require special privileges)")
+public class AdminResource extends JaxRsResourceBase {
+
+ private final AdminPaymentApi adminPaymentApi;
+
+ @Inject
+ public AdminResource(final JaxrsUriBuilder uriBuilder, final TagUserApi tagUserApi, final CustomFieldUserApi customFieldUserApi, final AuditUserApi auditUserApi, final AccountUserApi accountUserApi, final PaymentApi paymentApi, final AdminPaymentApi adminPaymentApi, final Clock clock, final Context context) {
+ super(uriBuilder, tagUserApi, customFieldUserApi, auditUserApi, accountUserApi, paymentApi, clock, context);
+ this.adminPaymentApi = adminPaymentApi;
+ }
+
+
+ @PUT
+ @Consumes(APPLICATION_JSON)
+ @Produces(APPLICATION_JSON)
+ @Path("/payments/{paymentId:" + UUID_PATTERN + "}/transactions/{paymentTransactionId:" + UUID_PATTERN + "}")
+ @ApiOperation(value = "Update existing paymentTransaction and associated payment state")
+ @ApiResponses(value = {@ApiResponse(code = 400, message = "Invalid account data supplied")})
+ public Response updatePaymentTransactionState(final AdminPaymentJson json,
+ @PathParam("paymentId") final String paymentIdStr,
+ @PathParam("paymentTransactionId") final String paymentTransactionIdStr,
+ @HeaderParam(HDR_CREATED_BY) final String createdBy,
+ @HeaderParam(HDR_REASON) final String reason,
+ @HeaderParam(HDR_COMMENT) final String comment,
+ @javax.ws.rs.core.Context final HttpServletRequest request) throws PaymentApiException {
+
+ final CallContext callContext = context.createContext(createdBy, reason, comment, request);
+
+ final Payment payment = paymentApi.getPayment(UUID.fromString(paymentIdStr), false, ImmutableList.<PluginProperty>of(), callContext);
+
+ final UUID paymentTransactionId = UUID.fromString(paymentTransactionIdStr);
+
+ final PaymentTransaction paymentTransaction = Iterables.tryFind(payment.getTransactions(), new Predicate<PaymentTransaction>() {
+ @Override
+ public boolean apply(final PaymentTransaction input) {
+ return input.getId().equals(paymentTransactionId);
+ }
+ }).orNull();
+
+ adminPaymentApi.fixPaymentTransactionState(payment, paymentTransaction, TransactionStatus.valueOf(json.getTransactionStatus()),
+ json.getLastSuccessPaymentState(), json.getCurrentPaymentStateName(), ImmutableList.<PluginProperty>of(), callContext);
+ return Response.status(Status.OK).build();
+ }
+
+}
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/JaxrsResource.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/JaxrsResource.java
index e7a52cf..4c5dabb 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/JaxrsResource.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/JaxrsResource.java
@@ -117,6 +117,9 @@ public interface JaxrsResource {
public static final String PAGINATION = "pagination";
+ public static final String ADMIN = "admin";
+ public static final String ADMIN_PATH = PREFIX + "/" + ADMIN;
+
public static final String ACCOUNTS = "accounts";
public static final String ACCOUNTS_PATH = PREFIX + "/" + ACCOUNTS;
diff --git a/payment/src/main/java/org/killbill/billing/payment/api/DefaultAdminPaymentApi.java b/payment/src/main/java/org/killbill/billing/payment/api/DefaultAdminPaymentApi.java
new file mode 100644
index 0000000..a256c61
--- /dev/null
+++ b/payment/src/main/java/org/killbill/billing/payment/api/DefaultAdminPaymentApi.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2014-2015 Groupon, Inc
+ * Copyright 2014-2015 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.util.UUID;
+
+import javax.annotation.Nullable;
+import javax.inject.Inject;
+
+import org.killbill.billing.callcontext.InternalCallContext;
+import org.killbill.billing.payment.dao.PaymentDao;
+import org.killbill.billing.util.callcontext.CallContext;
+import org.killbill.billing.util.callcontext.InternalCallContextFactory;
+import org.killbill.commons.locker.GlobalLocker;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class DefaultAdminPaymentApi implements AdminPaymentApi {
+
+ private static final Logger log = LoggerFactory.getLogger(DefaultAdminPaymentApi.class);
+
+ private final PaymentDao paymentDao;
+ private final GlobalLocker locker;
+ private final InternalCallContextFactory internalCallContextFactory;
+
+ @Inject
+ public DefaultAdminPaymentApi(final PaymentDao paymentDao, final InternalCallContextFactory internalCallContextFactory, final GlobalLocker locker) {
+ this.paymentDao = paymentDao;
+ this.internalCallContextFactory = internalCallContextFactory;
+ this.locker = locker;
+ }
+
+ @Override
+ public void fixPaymentTransactionState(final Payment payment, PaymentTransaction paymentTransaction, TransactionStatus transactionStatus, @Nullable String lastSuccessPaymentState, String currentPaymentStateName,
+ Iterable<PluginProperty> properties, CallContext callContext)
+ throws PaymentApiException {
+
+ final InternalCallContext internalCallContext = internalCallContextFactory.createInternalCallContext(payment.getAccountId(), callContext);
+ paymentDao.updatePaymentAndTransactionOnCompletion(payment.getAccountId(), payment.getId(), paymentTransaction.getTransactionType(),
+ currentPaymentStateName, lastSuccessPaymentState, paymentTransaction.getId(),
+ transactionStatus, paymentTransaction.getProcessedAmount(), paymentTransaction.getProcessedCurrency(),
+ paymentTransaction.getGatewayErrorCode(), paymentTransaction.getGatewayErrorMsg(), internalCallContext);
+ }
+}
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 7bcf32e..d51fe70 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
@@ -29,6 +29,8 @@ import javax.inject.Provider;
import org.killbill.automaton.DefaultStateMachineConfig;
import org.killbill.automaton.StateMachineConfig;
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.DefaultPaymentApi;
import org.killbill.billing.payment.api.DefaultPaymentGatewayApi;
import org.killbill.billing.payment.api.PaymentApi;
@@ -175,6 +177,7 @@ public class PaymentModule extends KillBillModule {
bind(PaymentApi.class).to(DefaultPaymentApi.class).asEagerSingleton();
bind(PaymentGatewayApi.class).to(DefaultPaymentGatewayApi.class).asEagerSingleton();
+ bind(AdminPaymentApi.class).to(DefaultAdminPaymentApi.class).asEagerSingleton();
bind(InvoiceHandler.class).asEagerSingleton();
bind(PaymentTagHandler.class).asEagerSingleton();
bind(PaymentService.class).to(DefaultPaymentService.class).asEagerSingleton();