Details
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 17ce949..bc87d1e 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
@@ -123,13 +123,4 @@ public abstract class ComboPaymentResource extends JaxRsResourceBase {
paymentData.getPluginDetail(), pluginProperties, callContext);
}
- protected Payment getPaymentByIdOrKey(@Nullable final String paymentIdStr, @Nullable final String externalKey, final Iterable<PluginProperty> pluginProperties, final TenantContext tenantContext) throws PaymentApiException {
- Preconditions.checkArgument(paymentIdStr != null || externalKey != null, "Need to set either paymentId or payment externalKey");
- if (paymentIdStr != null) {
- final UUID paymentId = UUID.fromString(paymentIdStr);
- return paymentApi.getPayment(paymentId, false, false, pluginProperties, tenantContext);
- } else {
- return paymentApi.getPaymentByExternalKey(externalKey, false, false, pluginProperties, tenantContext);
- }
- }
}
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 440d83a..45e0cb4 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
@@ -19,6 +19,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;
@@ -33,11 +34,13 @@ import javax.ws.rs.DefaultValue;
import javax.ws.rs.GET;
import javax.ws.rs.HeaderParam;
import javax.ws.rs.POST;
+import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
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.killbill.billing.ObjectType;
@@ -51,6 +54,7 @@ import org.killbill.billing.jaxrs.json.CustomFieldJson;
import org.killbill.billing.jaxrs.json.InvoiceItemJson;
import org.killbill.billing.jaxrs.json.InvoicePaymentJson;
import org.killbill.billing.jaxrs.json.InvoicePaymentTransactionJson;
+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;
@@ -69,6 +73,7 @@ import org.killbill.billing.util.audit.AccountAuditLogs;
import org.killbill.billing.util.callcontext.CallContext;
import org.killbill.billing.util.callcontext.TenantContext;
import org.killbill.clock.Clock;
+import org.killbill.commons.metrics.MetricTag;
import org.killbill.commons.metrics.TimedResource;
import com.google.common.base.Predicate;
@@ -258,6 +263,62 @@ public class InvoicePaymentResource extends JaxRsResourceBase {
return uriBuilder.buildResponse(uriInfo, InvoicePaymentResource.class, "getInvoicePayment", result.getId(), request);
}
+
+ @TimedResource(name = "completeInvoicePaymentTransaction")
+ @PUT
+ @Path("/{paymentId:" + UUID_PATTERN + "}")
+ @Consumes(APPLICATION_JSON)
+ @Produces(APPLICATION_JSON)
+ @ApiOperation(value = "Complete an existing transaction")
+ @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 completeInvoicePaymentTransaction(final PaymentTransactionJson json,
+ @PathParam("paymentId") final String paymentIdStr,
+ @QueryParam(QUERY_PAYMENT_CONTROL_PLUGIN_NAME) final List<String> paymentControlPluginNames,
+ @QueryParam(QUERY_PLUGIN_PROPERTY) final List<String> pluginPropertiesString,
+ @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 UriInfo uriInfo,
+ @javax.ws.rs.core.Context final HttpServletRequest request) throws PaymentApiException, AccountApiException {
+
+ final TenantContext tenantContext = context.createTenantContextNoAccountId(request);
+
+ final UUID paymentId = UUID.fromString(paymentIdStr);
+
+ final Payment payment = paymentApi.getPayment(paymentId, false, false, ImmutableList.<PluginProperty>of(), tenantContext);
+ final List<InvoicePayment> invoicePayments = invoicePaymentApi.getInvoicePayments(paymentId, tenantContext);
+
+ final InvoicePayment originalInvoicePaymentAttempt = Iterables.tryFind(invoicePayments, new Predicate<InvoicePayment>() {
+ @Override
+ public boolean apply(final InvoicePayment input) {
+ return input.getType() == InvoicePaymentType.ATTEMPT && !input.isSuccess();
+ }
+ }).orNull();
+
+ final UUID invoiceId = originalInvoicePaymentAttempt != null ? originalInvoicePaymentAttempt.getInvoiceId() : null;
+ if (invoiceId == null) {
+ 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 List<String> controlPluginNames = new ArrayList<String>();
+ controlPluginNames.add("__INVOICE_PAYMENT_CONTROL_PLUGIN__");
+ controlPluginNames.addAll(paymentControlPluginNames);
+
+ return completeTransactionInternal(json, payment, controlPluginNames, pluginProperties, tenantContext, createdBy, reason, comment, uriInfo, request);
+ }
+
+
@TimedResource
@GET
@Path("/{paymentId:" + UUID_PATTERN + "}/" + CUSTOM_FIELDS)
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 97c9b8c..07fbc86 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
@@ -52,6 +52,7 @@ 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.entitlement.api.BlockingState;
import org.killbill.billing.entitlement.api.BlockingStateType;
import org.killbill.billing.entitlement.api.EntitlementApiException;
@@ -64,6 +65,7 @@ import org.killbill.billing.jaxrs.json.BillingExceptionJson.StackTraceElementJso
import org.killbill.billing.jaxrs.json.BlockingStateJson;
import org.killbill.billing.jaxrs.json.CustomFieldJson;
import org.killbill.billing.jaxrs.json.JsonBase;
+import org.killbill.billing.jaxrs.json.PaymentTransactionJson;
import org.killbill.billing.jaxrs.json.PluginPropertyJson;
import org.killbill.billing.jaxrs.json.TagJson;
import org.killbill.billing.jaxrs.util.Context;
@@ -119,7 +121,6 @@ public abstract class JaxRsResourceBase implements JaxrsResource {
// Catalog API don't quite support multiple catalogs per tenant
protected static final String catalogName = "unused";
-
protected static final ObjectMapper mapper = new ObjectMapper();
protected final JaxrsUriBuilder uriBuilder;
@@ -183,9 +184,6 @@ public abstract class JaxRsResourceBase implements JaxrsResource {
return Response.status(Status.OK).build();
}
-
-
-
protected Response getTags(final UUID accountId, final UUID taggedObjectId, final AuditMode auditMode, final boolean includeDeleted, final TenantContext context) throws TagDefinitionApiException {
final List<Tag> tags = tagUserApi.getTagsForObject(taggedObjectId, getObjectType(), includeDeleted, context);
return createTagResponse(accountId, tags, auditMode, context);
@@ -208,7 +206,6 @@ public abstract class JaxRsResourceBase implements JaxrsResource {
return Response.status(Response.Status.OK).entity(result).build();
}
-
protected Response createTags(final UUID id,
final String tagList,
final UriInfo uriInfo,
@@ -361,8 +358,80 @@ public abstract class JaxRsResourceBase implements JaxrsResource {
}
}
+ protected Payment getPaymentByIdOrKey(@Nullable final String paymentIdStr, @Nullable final String externalKey, final Iterable<PluginProperty> pluginProperties, final TenantContext tenantContext) throws PaymentApiException {
+ Preconditions.checkArgument(paymentIdStr != null || externalKey != null, "Need to set either paymentId or payment externalKey");
+ if (paymentIdStr != null) {
+ final UUID paymentId = UUID.fromString(paymentIdStr);
+ return paymentApi.getPayment(paymentId, false, false, pluginProperties, tenantContext);
+ } else {
+ return paymentApi.getPaymentByExternalKey(externalKey, false, false, pluginProperties, tenantContext);
+ }
+ }
+
+
+ protected Response completeTransactionInternal(final PaymentTransactionJson json,
+ final Payment initialPayment,
+ final List<String> paymentControlPluginNames,
+ final Iterable<PluginProperty> pluginProperties,
+ final TenantContext contextNoAccountId,
+ final String createdBy,
+ final String reason,
+ final String comment,
+ final UriInfo uriInfo,
+ final HttpServletRequest request) throws PaymentApiException, AccountApiException {
+
+ final Account account = accountUserApi.getAccountById(initialPayment.getAccountId(), contextNoAccountId);
+ final BigDecimal amount = json == null ? null : json.getAmount();
+ final Currency currency = json == null || json.getCurrency() == null ? null : Currency.valueOf(json.getCurrency());
+
+ final CallContext callContext = context.createCallContextWithAccountId(account.getId(), createdBy, reason, comment, request);
+
+ final PaymentTransaction pendingOrSuccessTransaction = lookupPendingOrSuccessTransaction(initialPayment,
+ 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 uriBuilder.buildResponse(uriInfo, PaymentResource.class, "getPayment", pendingOrSuccessTransaction.getPaymentId(), request);
+ }
+
+ final PaymentTransaction pendingTransaction = pendingOrSuccessTransaction;
+ final PaymentOptions paymentOptions = createControlPluginApiPaymentOptions(paymentControlPluginNames);
+ final Payment result;
+ switch (pendingTransaction.getTransactionType()) {
+ case AUTHORIZE:
+ result = paymentApi.createAuthorizationWithPaymentControl(account, initialPayment.getPaymentMethodId(), initialPayment.getId(), amount, currency,
+ initialPayment.getExternalKey(), pendingTransaction.getExternalKey(),
+ pluginProperties, paymentOptions, callContext);
+ break;
+ case CAPTURE:
+ result = paymentApi.createCaptureWithPaymentControl(account, initialPayment.getId(), amount, currency, pendingTransaction.getExternalKey(),
+ pluginProperties, paymentOptions, callContext);
+ break;
+ case PURCHASE:
+ result = paymentApi.createPurchaseWithPaymentControl(account, initialPayment.getPaymentMethodId(), initialPayment.getId(), amount, currency,
+ initialPayment.getExternalKey(), pendingTransaction.getExternalKey(),
+ pluginProperties, paymentOptions, callContext);
+ break;
+ case CREDIT:
+ result = paymentApi.createCreditWithPaymentControl(account, initialPayment.getPaymentMethodId(), initialPayment.getId(), amount, currency,
+ initialPayment.getExternalKey(), pendingTransaction.getExternalKey(),
+ pluginProperties, paymentOptions, callContext);
+ break;
+ case REFUND:
+ result = paymentApi.createRefundWithPaymentControl(account, initialPayment.getId(), amount, currency,
+ pendingTransaction.getExternalKey(), pluginProperties, paymentOptions, callContext);
+ break;
+ default:
+ return Response.status(Status.PRECONDITION_FAILED).entity("TransactionType " + pendingTransaction.getTransactionType() + " cannot be completed").build();
+ }
+ return createPaymentResponse(uriInfo, result, pendingTransaction.getTransactionType(), pendingTransaction.getExternalKey(), request);
+
+ }
+
+
protected PaymentTransaction lookupPendingOrSuccessTransaction(final Payment initialPayment, @Nullable final String transactionId, @Nullable final String transactionExternalKey, @Nullable final String transactionType) throws PaymentApiException {
- final Collection<PaymentTransaction> pendingTransaction = Collections2.filter(initialPayment.getTransactions(), new Predicate<PaymentTransaction>() {
+ final Collection<PaymentTransaction> pendingTransaction = Collections2.filter(initialPayment.getTransactions(), new Predicate<PaymentTransaction>() {
@Override
public boolean apply(final PaymentTransaction input) {
if (input.getTransactionStatus() != TransactionStatus.PENDING && input.getTransactionStatus() != TransactionStatus.SUCCESS) {
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 c5c98e8..bf63aaa 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
@@ -17,7 +17,6 @@
package org.killbill.billing.jaxrs.resources;
-import java.math.BigDecimal;
import java.net.URI;
import java.util.HashMap;
import java.util.List;
@@ -59,9 +58,7 @@ 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.payment.api.TransactionType;
import org.killbill.billing.util.api.AuditUserApi;
import org.killbill.billing.util.api.CustomFieldApiException;
@@ -263,9 +260,11 @@ public class PaymentResource extends ComboPaymentResource {
@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 {
- return completeTransactionInternal(json, paymentIdStr, paymentControlPluginNames, pluginPropertiesString, createdBy, reason, comment, uriInfo, request);
+ return completeTransactionInternalWithoutPayment(json, paymentIdStr, paymentControlPluginNames, pluginPropertiesString, createdBy, reason, comment, uriInfo, request);
}
+
+
@TimedResource(name = "completeTransaction")
@PUT
@Consumes(APPLICATION_JSON)
@@ -286,7 +285,7 @@ public class PaymentResource extends ComboPaymentResource {
@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 {
- return completeTransactionInternal(json, null, paymentControlPluginNames, pluginPropertiesString, createdBy, reason, comment, uriInfo, request);
+ return completeTransactionInternalWithoutPayment(json, null, paymentControlPluginNames, pluginPropertiesString, createdBy, reason, comment, uriInfo, request);
}
@@ -294,71 +293,6 @@ public class PaymentResource extends ComboPaymentResource {
- private Response completeTransactionInternal(final PaymentTransactionJson json,
- @Nullable final String paymentIdStr,
- final List<String> paymentControlPluginNames,
- final Iterable<String> pluginPropertiesString,
- final String createdBy,
- final String reason,
- final String comment,
- final UriInfo uriInfo,
- final HttpServletRequest request) throws PaymentApiException, AccountApiException {
-
- final Iterable<PluginProperty> pluginPropertiesFromBody = extractPluginProperties(json.getProperties());
-
- final Iterable<PluginProperty> pluginPropertiesFromQuery = extractPluginProperties(pluginPropertiesString);
-
- final Iterable<PluginProperty> pluginProperties = Iterables.concat(pluginPropertiesFromQuery, pluginPropertiesFromBody);
-
- final CallContext callContext = context.createCallContextNoAccountId(createdBy, reason, comment, request);
- final Payment initialPayment = getPaymentByIdOrKey(paymentIdStr, json == null ? null : json.getPaymentExternalKey(), pluginProperties, callContext);
-
- final Account account = accountUserApi.getAccountById(initialPayment.getAccountId(), callContext);
- final BigDecimal amount = json == null ? null : json.getAmount();
- final Currency currency = json == null || json.getCurrency() == null ? null : Currency.valueOf(json.getCurrency());
-
- final PaymentTransaction pendingOrSuccessTransaction = lookupPendingOrSuccessTransaction(initialPayment,
- 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 uriBuilder.buildResponse(uriInfo, PaymentResource.class, "getPayment", pendingOrSuccessTransaction.getPaymentId(), request);
- }
-
-
- final PaymentTransaction pendingTransaction = pendingOrSuccessTransaction;
- final PaymentOptions paymentOptions = createControlPluginApiPaymentOptions(paymentControlPluginNames);
- final Payment result;
- switch (pendingTransaction.getTransactionType()) {
- case AUTHORIZE:
- result = paymentApi.createAuthorizationWithPaymentControl(account, initialPayment.getPaymentMethodId(), initialPayment.getId(), amount, currency,
- initialPayment.getExternalKey(), pendingTransaction.getExternalKey(),
- pluginProperties, paymentOptions, callContext);
- break;
- case CAPTURE:
- result = paymentApi.createCaptureWithPaymentControl(account, initialPayment.getId(), amount, currency, pendingTransaction.getExternalKey(),
- pluginProperties, paymentOptions, callContext);
- break;
- case PURCHASE:
- result = paymentApi.createPurchaseWithPaymentControl(account, initialPayment.getPaymentMethodId(), initialPayment.getId(), amount, currency,
- initialPayment.getExternalKey(), pendingTransaction.getExternalKey(),
- pluginProperties, paymentOptions, callContext);
- break;
- case CREDIT:
- result = paymentApi.createCreditWithPaymentControl(account, initialPayment.getPaymentMethodId(), initialPayment.getId(), amount, currency,
- initialPayment.getExternalKey(), pendingTransaction.getExternalKey(),
- pluginProperties, paymentOptions, callContext);
- break;
- case REFUND:
- result = paymentApi.createRefundWithPaymentControl(account, initialPayment.getId(), amount, currency,
- pendingTransaction.getExternalKey(), pluginProperties, paymentOptions, callContext);
- break;
- default:
- return Response.status(Status.PRECONDITION_FAILED).entity("TransactionType " + pendingTransaction.getTransactionType() + " cannot be completed").build();
- }
- return createPaymentResponse(uriInfo, result, pendingTransaction.getTransactionType(), pendingTransaction.getExternalKey(), request);
- }
@TimedResource(name = "captureAuthorization")
@POST
@@ -950,4 +884,27 @@ public class PaymentResource extends ComboPaymentResource {
protected ObjectType getObjectType() {
return ObjectType.PAYMENT;
}
+
+ private Response completeTransactionInternalWithoutPayment(final PaymentTransactionJson json,
+ @Nullable final String paymentIdStr,
+ final List<String> paymentControlPluginNames,
+ final Iterable<String> pluginPropertiesString,
+ final String createdBy,
+ final String reason,
+ final String comment,
+ final UriInfo uriInfo,
+ final HttpServletRequest request) throws PaymentApiException, AccountApiException {
+
+ final Iterable<PluginProperty> pluginPropertiesFromBody = extractPluginProperties(json.getProperties());
+
+ final Iterable<PluginProperty> pluginPropertiesFromQuery = extractPluginProperties(pluginPropertiesString);
+
+ final Iterable<PluginProperty> pluginProperties = Iterables.concat(pluginPropertiesFromQuery, pluginPropertiesFromBody);
+
+ final CallContext callContextNoAccountId = context.createCallContextNoAccountId(createdBy, reason, comment, request);
+ final Payment initialPayment = getPaymentByIdOrKey(paymentIdStr, json == null ? null : json.getPaymentExternalKey(), pluginProperties, callContextNoAccountId);
+
+ return completeTransactionInternal(json, initialPayment, paymentControlPluginNames, pluginProperties, callContextNoAccountId, createdBy, reason, comment, uriInfo, request);
+ }
+
}
diff --git a/profiles/killbill/src/main/resources/killbill-server.properties b/profiles/killbill/src/main/resources/killbill-server.properties
index 6208aa4..213957d 100644
--- a/profiles/killbill/src/main/resources/killbill-server.properties
+++ b/profiles/killbill/src/main/resources/killbill-server.properties
@@ -23,7 +23,7 @@
# KILLBILL GENERIC PROPERTIES
#
# Database config
-org.killbill.dao.url=jdbc:mysql://127.0.0.1:3306/killbill
+org.killbill.dao.url=jdbc:mysql://127.0.0.1:3306/killbill_0_19_x
org.killbill.dao.user=root
org.killbill.dao.password=root
org.killbill.dao.logLevel=DEBUG
@@ -62,7 +62,7 @@ org.killbill.tenant.broadcast.rate=1s
# PLUGIN SPECIFIC PROPERTIES
#
# Database config (OSGI plugins)
-org.killbill.billing.osgi.dao.url=jdbc:mysql://127.0.0.1:3306/killbill
+org.killbill.billing.osgi.dao.url=jdbc:mysql://127.0.0.1:3306/killbill_0_19_x
org.killbill.billing.osgi.dao.user=root
org.killbill.billing.osgi.dao.password=root
@@ -88,3 +88,5 @@ org.killbill.payment.retry.days=1,1,1
org.killbill.notificationq.analytics.tableName=analytics_notifications
org.killbill.notificationq.analytics.historyTableName=analytics_notifications_history
+
+org.killbill.osgi.bundle.install.dir=/Users/sbrossier/Documents/KillBillConfig/bundles_0_19_x/
diff --git a/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestInvoicePayment.java b/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestInvoicePayment.java
index e7c5755..9803ca8 100644
--- a/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestInvoicePayment.java
+++ b/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestInvoicePayment.java
@@ -45,6 +45,7 @@ import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
import com.google.inject.Inject;
import static org.testng.Assert.assertEquals;
@@ -70,6 +71,32 @@ public class TestInvoicePayment extends TestJaxrsBase {
Assert.assertTrue(retrievedPaymentJson.equals((Payment) paymentJson));
}
+
+ @Test(groups = "slow")
+ public void testInvoicePaymentCompletion() throws Exception {
+ mockPaymentProviderPlugin.makeNextPaymentPending();
+
+ final InvoicePayment paymentJson = setupScenarioWithPayment();
+
+ final Payment retrievedPaymentJson = killBillClient.getPayment(paymentJson.getPaymentId(), false, requestOptions);
+ Assert.assertTrue(retrievedPaymentJson.equals((Payment) paymentJson));
+ Assert.assertEquals(retrievedPaymentJson.getTransactions().size(), 1);
+ Assert.assertEquals(retrievedPaymentJson.getTransactions().get(0).getStatus(), "PENDING");
+
+ final PaymentTransaction completeTransactionByPaymentId = new PaymentTransaction();
+ completeTransactionByPaymentId.setPaymentId(retrievedPaymentJson.getPaymentId());
+
+ final Account accountWithBalance = killBillClient.getAccount(paymentJson.getAccountId(), true, false, requestOptions);
+ Assert.assertTrue(accountWithBalance.getAccountBalance().compareTo(BigDecimal.ZERO) > 0);
+
+ final Payment completedPayment = killBillClient.completeInvoicePayment(completeTransactionByPaymentId, null, ImmutableMap.<String, String>of(), requestOptions);
+ Assert.assertEquals(completedPayment.getTransactions().get(0).getStatus(), "SUCCESS");
+
+ final Account accountWithBalance2 = killBillClient.getAccount(paymentJson.getAccountId(), true, false, requestOptions);
+ Assert.assertEquals(accountWithBalance2.getAccountBalance().compareTo(BigDecimal.ZERO), 0);
+
+ }
+
@Test(groups = "slow", description = "Can create a full refund with no adjustment")
public void testFullRefundWithNoAdjustment() throws Exception {
final InvoicePayment invoicePaymentJson = setupScenarioWithPayment();