diff --git a/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/PaymentResource.java b/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/PaymentResource.java
index 87fcff3..3f49504 100644
--- a/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/PaymentResource.java
+++ b/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/PaymentResource.java
@@ -16,8 +16,11 @@
package com.ning.billing.jaxrs.resources;
+import java.math.BigDecimal;
import java.util.ArrayList;
+import java.util.HashMap;
import java.util.List;
+import java.util.Map;
import java.util.UUID;
import javax.ws.rs.Consumes;
@@ -37,6 +40,7 @@ import com.ning.billing.account.api.Account;
import com.ning.billing.account.api.AccountApiException;
import com.ning.billing.account.api.AccountUserApi;
import com.ning.billing.jaxrs.json.CustomFieldJson;
+import com.ning.billing.jaxrs.json.InvoiceItemJsonSimple;
import com.ning.billing.jaxrs.json.RefundJson;
import com.ning.billing.jaxrs.util.Context;
import com.ning.billing.jaxrs.util.JaxrsUriBuilder;
@@ -112,8 +116,18 @@ public class PaymentResource extends JaxRsResourceBase {
final Refund result;
if (json.isAdjusted()) {
- result = paymentApi.createRefundWithAdjustment(account, paymentUuid, json.getRefundAmount(), context.createContext(createdBy, reason, comment));
+ if (json.getAdjustments() != null && json.getAdjustments().size() > 0) {
+ final Map<UUID, BigDecimal> adjustments = new HashMap<UUID, BigDecimal>();
+ for (final InvoiceItemJsonSimple item : json.getAdjustments()) {
+ adjustments.put(UUID.fromString(item.getInvoiceItemId()), item.getAmount());
+ }
+ result = paymentApi.createRefundWithItemsAdjustments(account, paymentUuid, adjustments, context.createContext(createdBy, reason, comment));
+ } else {
+ // Invoice adjustment
+ result = paymentApi.createRefundWithAdjustment(account, paymentUuid, json.getRefundAmount(), context.createContext(createdBy, reason, comment));
+ }
} else {
+ // Refund without adjustment
result = paymentApi.createRefund(account, paymentUuid, json.getRefundAmount(), context.createContext(createdBy, reason, comment));
}
diff --git a/payment/src/main/java/com/ning/billing/payment/core/RefundProcessor.java b/payment/src/main/java/com/ning/billing/payment/core/RefundProcessor.java
index ff4ed16..63706ba 100644
--- a/payment/src/main/java/com/ning/billing/payment/core/RefundProcessor.java
+++ b/payment/src/main/java/com/ning/billing/payment/core/RefundProcessor.java
@@ -36,6 +36,7 @@ import com.ning.billing.account.api.Account;
import com.ning.billing.account.api.AccountApiException;
import com.ning.billing.account.api.AccountUserApi;
import com.ning.billing.invoice.api.InvoiceApiException;
+import com.ning.billing.invoice.api.InvoiceItem;
import com.ning.billing.invoice.api.InvoicePaymentApi;
import com.ning.billing.payment.api.DefaultRefund;
import com.ning.billing.payment.api.PaymentApiException;
@@ -87,12 +88,12 @@ public class RefundProcessor extends ProcessorBase {
/**
* Create a refund and adjust the invoice or invoice items as necessary.
*
- * @param account account to refund
- * @param paymentId payment associated with that refund
- * @param specifiedRefundAmount amount to refund. If null, the amount will be the sum of adjusted invoice items
- * @param isAdjusted whether the refund should trigger an invoice or invoice item adjustment
+ * @param account account to refund
+ * @param paymentId payment associated with that refund
+ * @param specifiedRefundAmount amount to refund. If null, the amount will be the sum of adjusted invoice items
+ * @param isAdjusted whether the refund should trigger an invoice or invoice item adjustment
* @param invoiceItemIdsWithAmounts invoice item ids and associated amounts to adjust
- * @param context the call context
+ * @param context the call context
* @return the created context
* @throws PaymentApiException
*/
@@ -105,7 +106,7 @@ public class RefundProcessor extends ProcessorBase {
@Override
public Refund doOperation() throws PaymentApiException {
// First, compute the refund amount, if necessary
- final BigDecimal refundAmount = computeRefundAmount(specifiedRefundAmount, invoiceItemIdsWithAmounts);
+ final BigDecimal refundAmount = computeRefundAmount(paymentId, specifiedRefundAmount, invoiceItemIdsWithAmounts);
try {
@@ -180,14 +181,18 @@ public class RefundProcessor extends ProcessorBase {
/**
* Compute the refund amount (computed from the invoice or invoice items as necessary).
*
+ * @param paymentId payment id associated with this refund
* @param specifiedRefundAmount amount to refund. If null, the amount will be the sum of adjusted invoice items
* @param invoiceItemIdsWithAmounts invoice item ids and associated amounts to adjust
* @return the refund amount
*/
- private BigDecimal computeRefundAmount(@Nullable final BigDecimal specifiedRefundAmount, final Map<UUID, BigDecimal> invoiceItemIdsWithAmounts) {
+ private BigDecimal computeRefundAmount(final UUID paymentId, @Nullable final BigDecimal specifiedRefundAmount, final Map<UUID, BigDecimal> invoiceItemIdsWithAmounts) {
+ final List<InvoiceItem> items = invoicePaymentApi.getInvoiceForPaymentId(paymentId).getInvoiceItems();
+
BigDecimal amountFromItems = BigDecimal.ZERO;
- for (final BigDecimal itemAmount : invoiceItemIdsWithAmounts.values()) {
- amountFromItems = amountFromItems.add(itemAmount);
+ for (final UUID itemId : invoiceItemIdsWithAmounts.keySet()) {
+ amountFromItems = amountFromItems.add(Objects.firstNonNull(invoiceItemIdsWithAmounts.get(itemId),
+ getAmountFromItem(items, itemId)));
}
// Sanity check: if some items were specified, then the sum should be equal to specified refund amount, if specified
@@ -198,6 +203,16 @@ public class RefundProcessor extends ProcessorBase {
return Objects.firstNonNull(specifiedRefundAmount, amountFromItems);
}
+ private BigDecimal getAmountFromItem(final List<InvoiceItem> items, final UUID itemId) {
+ for (final InvoiceItem item : items) {
+ if (item.getId().equals(itemId)) {
+ return item.getAmount();
+ }
+ }
+
+ throw new IllegalArgumentException("Unable to find invoice item for id " + itemId);
+ }
+
public Refund getRefund(final UUID refundId)
throws PaymentApiException {
RefundModelDao result = paymentDao.getRefund(refundId);
diff --git a/server/src/test/java/com/ning/billing/jaxrs/TestJaxrsBase.java b/server/src/test/java/com/ning/billing/jaxrs/TestJaxrsBase.java
index f45c6db..c5334d9 100644
--- a/server/src/test/java/com/ning/billing/jaxrs/TestJaxrsBase.java
+++ b/server/src/test/java/com/ning/billing/jaxrs/TestJaxrsBase.java
@@ -13,10 +13,9 @@
* License for the specific language governing permissions and limitations
* under the License.
*/
+
package com.ning.billing.jaxrs;
-import javax.annotation.Nullable;
-import javax.ws.rs.core.Response.Status;
import java.io.IOException;
import java.lang.reflect.Method;
import java.math.BigDecimal;
@@ -31,6 +30,9 @@ import java.util.Map.Entry;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
+import javax.annotation.Nullable;
+import javax.ws.rs.core.Response.Status;
+
import org.eclipse.jetty.servlet.FilterHolder;
import org.joda.time.DateTime;
import org.skife.config.ConfigurationObjectFactory;
@@ -38,27 +40,16 @@ import org.skife.jdbi.v2.IDBI;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.testng.Assert;
-import org.testng.annotations.AfterMethod;
import org.testng.annotations.AfterSuite;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.BeforeSuite;
-import com.fasterxml.jackson.core.JsonGenerationException;
-import com.fasterxml.jackson.core.JsonParseException;
-import com.fasterxml.jackson.core.type.TypeReference;
-import com.fasterxml.jackson.databind.ObjectMapper;
-import com.fasterxml.jackson.databind.SerializationFeature;
-import com.fasterxml.jackson.datatype.joda.JodaModule;
-import com.google.common.collect.ImmutableMap;
-import com.google.inject.Module;
import com.ning.billing.KillbillTestSuiteWithEmbeddedDB;
-import com.ning.billing.account.api.BillCycleDay;
import com.ning.billing.account.glue.AccountModule;
import com.ning.billing.analytics.setup.AnalyticsModule;
import com.ning.billing.api.TestApiListener;
import com.ning.billing.beatrix.glue.BeatrixModule;
-import com.ning.billing.beatrix.integration.TestIntegration;
import com.ning.billing.catalog.api.BillingPeriod;
import com.ning.billing.catalog.api.Currency;
import com.ning.billing.catalog.api.PriceListSet;
@@ -102,7 +93,6 @@ import com.ning.billing.util.glue.CustomFieldModule;
import com.ning.billing.util.glue.GlobalLockerModule;
import com.ning.billing.util.glue.NotificationQueueModule;
import com.ning.billing.util.glue.TagStoreModule;
-import com.ning.billing.util.io.IOUtils;
import com.ning.http.client.AsyncCompletionHandler;
import com.ning.http.client.AsyncHttpClient;
import com.ning.http.client.AsyncHttpClient.BoundRequestBuilder;
@@ -112,11 +102,19 @@ import com.ning.http.client.Response;
import com.ning.jetty.core.CoreConfig;
import com.ning.jetty.core.server.HttpServer;
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.SerializationFeature;
+import com.fasterxml.jackson.datatype.joda.JodaModule;
+import com.google.common.collect.ImmutableMap;
+import com.google.inject.Module;
+
import static com.ning.billing.jaxrs.resources.JaxrsResource.QUERY_PAYMENT_METHOD_PLUGIN_INFO;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertNotNull;
public class TestJaxrsBase extends ServerTestSuiteWithEmbeddedDB {
+
protected static final String PLUGIN_NAME = "noop";
// STEPH
@@ -156,6 +154,7 @@ public class TestJaxrsBase extends ServerTestSuiteWithEmbeddedDB {
}
public static class TestKillbillGuiceListener extends KillbillGuiceListener {
+
private final MysqlTestingHelper helper;
private final Clock clock;
@@ -185,6 +184,7 @@ public class TestJaxrsBase extends ServerTestSuiteWithEmbeddedDB {
}
public static class InvoiceModuleWithMockSender extends DefaultInvoiceModule {
+
@Override
protected void installInvoiceNotifier() {
bind(InvoiceNotifier.class).to(NullInvoiceNotifier.class).asEagerSingleton();
@@ -192,6 +192,7 @@ public class TestJaxrsBase extends ServerTestSuiteWithEmbeddedDB {
}
public static class TestKillbillServerModule extends KillbillServerModule {
+
private final MysqlTestingHelper helper;
private final Clock clock;
@@ -207,6 +208,7 @@ public class TestJaxrsBase extends ServerTestSuiteWithEmbeddedDB {
}
private static final class PaymentMockModule extends PaymentModule {
+
@Override
protected void installPaymentProviderPlugins(final PaymentConfig config) {
install(new MockPaymentProviderPluginModule(PLUGIN_NAME));
@@ -327,7 +329,6 @@ public class TestJaxrsBase extends ServerTestSuiteWithEmbeddedDB {
return properties;
}
-
protected List<PaymentMethodProperties> getPaymentMethodPaypalProperties() {
final List<PaymentMethodProperties> properties = new ArrayList<PaymentMethodJson.PaymentMethodProperties>();
properties.add(new PaymentMethodProperties("type", "CreditCard", false));
@@ -336,7 +337,6 @@ public class TestJaxrsBase extends ServerTestSuiteWithEmbeddedDB {
return properties;
}
-
protected PaymentMethodJson getPaymentMethodJson(final String accountId, final List<PaymentMethodProperties> properties) {
final PaymentMethodPluginDetailJson info = new PaymentMethodPluginDetailJson(null, properties);
return new PaymentMethodJson(null, accountId, true, PLUGIN_NAME, info);
@@ -359,7 +359,6 @@ public class TestJaxrsBase extends ServerTestSuiteWithEmbeddedDB {
Response response = doPost(uri, baseJson, queryParams, DEFAULT_HTTP_TIMEOUT_SEC);
Assert.assertEquals(response.getStatusCode(), Status.CREATED.getStatusCode());
-
queryParams = new HashMap<String, String>();
queryParams.put(JaxrsResource.QUERY_EXTERNAL_KEY, input.getExternalKey());
response = doGet(JaxrsResource.ACCOUNTS_PATH, queryParams, DEFAULT_HTTP_TIMEOUT_SEC);
@@ -390,7 +389,6 @@ public class TestJaxrsBase extends ServerTestSuiteWithEmbeddedDB {
return objFromJson;
}
-
protected BundleJsonNoSubscriptions createBundle(final String accountId, final String key) throws Exception {
final BundleJsonNoSubscriptions input = new BundleJsonNoSubscriptions(null, accountId, key, null, null);
String baseJson = mapper.writeValueAsString(input);
@@ -415,7 +413,6 @@ public class TestJaxrsBase extends ServerTestSuiteWithEmbeddedDB {
final SubscriptionJsonNoEvents input = new SubscriptionJsonNoEvents(null, bundleId, null, productName, productCategory, billingPeriod, PriceListSet.DEFAULT_PRICELIST_NAME, null, null);
String baseJson = mapper.writeValueAsString(input);
-
final Map<String, String> queryParams = waitCompletion ? getQueryParamsForCallCompletion("5") : DEFAULT_EMPTY_QUERY;
Response response = doPost(JaxrsResource.SUBSCRIPTIONS_PATH, baseJson, queryParams, DEFAULT_HTTP_TIMEOUT_SEC);
Assert.assertEquals(response.getStatusCode(), Status.CREATED.getStatusCode());
@@ -472,45 +469,49 @@ public class TestJaxrsBase extends ServerTestSuiteWithEmbeddedDB {
}
protected InvoiceJsonSimple getInvoice(final String invoiceId) throws IOException {
+ return doGetInvoice(invoiceId, Boolean.FALSE, InvoiceJsonSimple.class);
+ }
+
+ protected InvoiceJsonWithItems getInvoiceWithItems(final String invoiceId) throws IOException {
+ return doGetInvoice(invoiceId, Boolean.TRUE, InvoiceJsonWithItems.class);
+ }
+
+ private <T> T doGetInvoice(final String invoiceId, final Boolean withItems, final Class<T> clazz) throws IOException {
final String uri = JaxrsResource.INVOICES_PATH + "/" + invoiceId;
- final Response response = doGet(uri, DEFAULT_EMPTY_QUERY, DEFAULT_HTTP_TIMEOUT_SEC);
+
+ final Map<String, String> queryParams = new HashMap<String, String>();
+ queryParams.put(JaxrsResource.QUERY_INVOICE_WITH_ITEMS, withItems.toString());
+
+ final Response response = doGet(uri, queryParams, DEFAULT_HTTP_TIMEOUT_SEC);
Assert.assertEquals(response.getStatusCode(), Status.OK.getStatusCode());
final String baseJson = response.getResponseBody();
- final InvoiceJsonSimple firstInvoiceJson = mapper.readValue(baseJson, InvoiceJsonSimple.class);
+ final T firstInvoiceJson = mapper.readValue(baseJson, clazz);
assertNotNull(firstInvoiceJson);
return firstInvoiceJson;
}
protected List<InvoiceJsonSimple> getInvoicesForAccount(final String accountId) throws IOException {
- final String invoicesURI = JaxrsResource.INVOICES_PATH;
-
- final Map<String, String> queryParams = new HashMap<String, String>();
- queryParams.put(JaxrsResource.QUERY_ACCOUNT_ID, accountId);
-
- final Response invoicesResponse = doGet(invoicesURI, queryParams, DEFAULT_HTTP_TIMEOUT_SEC);
- assertEquals(invoicesResponse.getStatusCode(), Status.OK.getStatusCode());
-
- final String invoicesBaseJson = invoicesResponse.getResponseBody();
- final List<InvoiceJsonSimple> invoices = mapper.readValue(invoicesBaseJson, new TypeReference<List<InvoiceJsonSimple>>() {});
- assertNotNull(invoices);
-
- return invoices;
+ return doGetInvoicesForAccount(accountId, Boolean.FALSE, InvoiceJsonSimple.class);
}
protected List<InvoiceJsonWithItems> getInvoicesWithItemsForAccount(final String accountId) throws IOException {
+ return doGetInvoicesForAccount(accountId, Boolean.TRUE, InvoiceJsonWithItems.class);
+ }
+
+ private <T> List<T> doGetInvoicesForAccount(final String accountId, final Boolean withItems, final Class<T> clazz) throws IOException {
final String invoicesURI = JaxrsResource.INVOICES_PATH;
final Map<String, String> queryParams = new HashMap<String, String>();
queryParams.put(JaxrsResource.QUERY_ACCOUNT_ID, accountId);
- queryParams.put(JaxrsResource.QUERY_INVOICE_WITH_ITEMS, "true");
+ queryParams.put(JaxrsResource.QUERY_INVOICE_WITH_ITEMS, withItems.toString());
final Response invoicesResponse = doGet(invoicesURI, queryParams, DEFAULT_HTTP_TIMEOUT_SEC);
assertEquals(invoicesResponse.getStatusCode(), Status.OK.getStatusCode());
final String invoicesBaseJson = invoicesResponse.getResponseBody();
- final List<InvoiceJsonWithItems> invoices = mapper.readValue(invoicesBaseJson, new TypeReference<List<InvoiceJsonWithItems>>() {});
+ final List<T> invoices = mapper.readValue(invoicesBaseJson, new TypeReference<List<T>>() {});
assertNotNull(invoices);
return invoices;
@@ -658,9 +659,28 @@ public class TestJaxrsBase extends ServerTestSuiteWithEmbeddedDB {
}
protected RefundJson createRefund(final String paymentId, final BigDecimal amount) throws IOException {
+ return doCreateRefund(paymentId, amount, false, ImmutableMap.<String, BigDecimal>of());
+ }
+
+ protected RefundJson createRefundWithInvoiceAdjustment(final String paymentId, final BigDecimal amount) throws IOException {
+ return doCreateRefund(paymentId, amount, true, ImmutableMap.<String, BigDecimal>of());
+ }
+
+ protected RefundJson createRefundWithInvoiceItemAdjustment(final String paymentId, final String invoiceItemId, final BigDecimal amount) throws IOException {
+ final Map<String, BigDecimal> adjustments = new HashMap<String, BigDecimal>();
+ adjustments.put(invoiceItemId, amount);
+ return doCreateRefund(paymentId, amount, true, adjustments);
+ }
+
+ private RefundJson doCreateRefund(final String paymentId, final BigDecimal amount, final boolean adjusted, final Map<String, BigDecimal> itemAdjustments) throws IOException {
final String uri = JaxrsResource.PAYMENTS_PATH + "/" + paymentId + "/" + JaxrsResource.REFUNDS;
- final RefundJson refundJson = new RefundJson(null, paymentId, amount, false, null, null, null, null);
+ final List<InvoiceItemJsonSimple> adjustments = new ArrayList<InvoiceItemJsonSimple>();
+ for (final String itemId : itemAdjustments.keySet()) {
+ adjustments.add(new InvoiceItemJsonSimple(itemId, null, null, null, null, null, null, null, null, null,
+ itemAdjustments.get(itemId), null, null));
+ }
+ final RefundJson refundJson = new RefundJson(null, paymentId, amount, adjusted, null, null, adjustments, null);
final String baseJson = mapper.writeValueAsString(refundJson);
final Response response = doPost(uri, baseJson, DEFAULT_EMPTY_QUERY, DEFAULT_HTTP_TIMEOUT_SEC);
assertEquals(response.getStatusCode(), Status.CREATED.getStatusCode());
diff --git a/server/src/test/java/com/ning/billing/jaxrs/TestPayment.java b/server/src/test/java/com/ning/billing/jaxrs/TestPayment.java
index 0913fdc..14c4ae9 100644
--- a/server/src/test/java/com/ning/billing/jaxrs/TestPayment.java
+++ b/server/src/test/java/com/ning/billing/jaxrs/TestPayment.java
@@ -16,13 +16,18 @@
package com.ning.billing.jaxrs;
+import java.io.IOException;
import java.math.BigDecimal;
+import java.math.RoundingMode;
import java.util.List;
import org.testng.Assert;
import org.testng.annotations.Test;
import com.ning.billing.jaxrs.json.AccountJson;
+import com.ning.billing.jaxrs.json.InvoiceItemJsonSimple;
+import com.ning.billing.jaxrs.json.InvoiceJsonSimple;
+import com.ning.billing.jaxrs.json.InvoiceJsonWithItems;
import com.ning.billing.jaxrs.json.PaymentJsonSimple;
import com.ning.billing.jaxrs.json.PaymentMethodJson;
import com.ning.billing.jaxrs.json.RefundJson;
@@ -30,28 +35,141 @@ import com.ning.billing.jaxrs.json.RefundJson;
public class TestPayment extends TestJaxrsBase {
@Test(groups = "slow")
- public void testPaymentWithRefund() throws Exception {
+ public void testFullRefundWithNoAdjustment() throws Exception {
+ final PaymentJsonSimple paymentJsonSimple = setupScenarioWithPayment();
+
+ // Issue a refund for the full amount
+ final BigDecimal refundAmount = paymentJsonSimple.getAmount();
+ final BigDecimal expectedInvoiceBalance = refundAmount;
+
+ // Post and verify the refund
+ final RefundJson refundJsonCheck = createRefund(paymentJsonSimple.getPaymentId(), refundAmount);
+ verifyRefund(paymentJsonSimple, refundJsonCheck, refundAmount);
+
+ // Verify the invoice balance
+ verifyInvoice(paymentJsonSimple, expectedInvoiceBalance);
+ }
+
+ @Test(groups = "slow")
+ public void testPartialRefundWithNoAdjustment() throws Exception {
+ final PaymentJsonSimple paymentJsonSimple = setupScenarioWithPayment();
+
+ // Issue a refund for a fraction of the amount
+ final BigDecimal refundAmount = getFractionOfAmount(paymentJsonSimple.getAmount());
+ final BigDecimal expectedInvoiceBalance = refundAmount;
+
+ // Post and verify the refund
+ final RefundJson refundJsonCheck = createRefund(paymentJsonSimple.getPaymentId(), refundAmount);
+ verifyRefund(paymentJsonSimple, refundJsonCheck, refundAmount);
+
+ // Verify the invoice balance
+ verifyInvoice(paymentJsonSimple, expectedInvoiceBalance);
+ }
+
+ @Test(groups = "slow")
+ public void testFullRefundWithInvoiceAdjustment() throws Exception {
+ final PaymentJsonSimple paymentJsonSimple = setupScenarioWithPayment();
+
+ // Issue a refund for the full amount
+ final BigDecimal refundAmount = paymentJsonSimple.getAmount();
+ final BigDecimal expectedInvoiceBalance = BigDecimal.ZERO;
+
+ // Post and verify the refund
+ final RefundJson refundJsonCheck = createRefundWithInvoiceAdjustment(paymentJsonSimple.getPaymentId(), refundAmount);
+ verifyRefund(paymentJsonSimple, refundJsonCheck, refundAmount);
+
+ // Verify the invoice balance
+ verifyInvoice(paymentJsonSimple, expectedInvoiceBalance);
+ }
+
+ @Test(groups = "slow")
+ public void testPartialRefundWithInvoiceAdjustment() throws Exception {
+ final PaymentJsonSimple paymentJsonSimple = setupScenarioWithPayment();
+
+ // Issue a refund for a fraction of the amount
+ final BigDecimal refundAmount = getFractionOfAmount(paymentJsonSimple.getAmount());
+ final BigDecimal expectedInvoiceBalance = BigDecimal.ZERO;
+
+ // Post and verify the refund
+ final RefundJson refundJsonCheck = createRefundWithInvoiceAdjustment(paymentJsonSimple.getPaymentId(), refundAmount);
+ verifyRefund(paymentJsonSimple, refundJsonCheck, refundAmount);
+
+ // Verify the invoice balance
+ verifyInvoice(paymentJsonSimple, expectedInvoiceBalance);
+ }
+
+ @Test(groups = "slow")
+ public void testRefundWithFullInvoiceItemAdjustment() throws Exception {
+ final PaymentJsonSimple paymentJsonSimple = setupScenarioWithPayment();
+
+ // Get the individual items for the invoice
+ final InvoiceJsonWithItems invoice = getInvoiceWithItems(paymentJsonSimple.getInvoiceId());
+ final InvoiceItemJsonSimple itemToAdjust = invoice.getItems().get(0);
+
+ // Issue a refund for the full amount
+ final BigDecimal refundAmount = itemToAdjust.getAmount();
+ final BigDecimal expectedInvoiceBalance = BigDecimal.ZERO;
+
+ // Post and verify the refund
+ final RefundJson refundJsonCheck = createRefundWithInvoiceItemAdjustment(paymentJsonSimple.getPaymentId(),
+ itemToAdjust.getInvoiceItemId(),
+ null /* null means full adjustment for that item */);
+ verifyRefund(paymentJsonSimple, refundJsonCheck, refundAmount);
+
+ // Verify the invoice balance
+ verifyInvoice(paymentJsonSimple, expectedInvoiceBalance);
+ }
+
+ @Test(groups = "slow")
+ public void testPartialRefundWithInvoiceItemAdjustment() throws Exception {
+ final PaymentJsonSimple paymentJsonSimple = setupScenarioWithPayment();
+
+ // Get the individual items for the invoice
+ final InvoiceJsonWithItems invoice = getInvoiceWithItems(paymentJsonSimple.getInvoiceId());
+ final InvoiceItemJsonSimple itemToAdjust = invoice.getItems().get(0);
+
+ // Issue a refund for a fraction of the amount
+ final BigDecimal refundAmount = getFractionOfAmount(itemToAdjust.getAmount());
+ final BigDecimal expectedInvoiceBalance = BigDecimal.ZERO;
+
+ // Post and verify the refund
+ final RefundJson refundJsonCheck = createRefundWithInvoiceItemAdjustment(paymentJsonSimple.getPaymentId(),
+ itemToAdjust.getInvoiceItemId(),
+ refundAmount);
+ verifyRefund(paymentJsonSimple, refundJsonCheck, refundAmount);
+
+ // Verify the invoice balance
+ verifyInvoice(paymentJsonSimple, expectedInvoiceBalance);
+ }
+
+ private BigDecimal getFractionOfAmount(final BigDecimal amount) {
+ return amount.divide(BigDecimal.TEN).setScale(2, BigDecimal.ROUND_HALF_UP);
+ }
+
+ private PaymentJsonSimple setupScenarioWithPayment() throws Exception {
final AccountJson accountJson = createAccountWithPMBundleAndSubscriptionAndWaitForFirstInvoice();
final List<PaymentJsonSimple> firstPaymentForAccount = getPaymentsForAccount(accountJson.getAccountId());
Assert.assertEquals(firstPaymentForAccount.size(), 1);
- final String paymentId = firstPaymentForAccount.get(0).getPaymentId();
- final BigDecimal paymentAmount = firstPaymentForAccount.get(0).getAmount();
+ final PaymentJsonSimple paymentJsonSimple = firstPaymentForAccount.get(0);
// Check the PaymentMethod from paymentMethodId returned in the Payment object
- final String paymentMethodId = firstPaymentForAccount.get(0).getPaymentMethodId();
+ final String paymentMethodId = paymentJsonSimple.getPaymentMethodId();
final PaymentMethodJson paymentMethodJson = getPaymentMethodWithPluginInfo(paymentMethodId);
Assert.assertEquals(paymentMethodJson.getPaymentMethodId(), paymentMethodId);
Assert.assertEquals(paymentMethodJson.getAccountId(), accountJson.getAccountId());
Assert.assertNotNull(paymentMethodJson.getPluginInfo().getExternalPaymentId());
// Verify the refunds
- final List<RefundJson> objRefundFromJson = getRefundsForPayment(paymentId);
+ final List<RefundJson> objRefundFromJson = getRefundsForPayment(paymentJsonSimple.getPaymentId());
Assert.assertEquals(objRefundFromJson.size(), 0);
+ return paymentJsonSimple;
+ }
- // Issue a refund for the full amount
- final RefundJson refundJsonCheck = createRefund(paymentId, paymentAmount);
+ private void verifyRefund(final PaymentJsonSimple paymentJsonSimple, final RefundJson refundJsonCheck, final BigDecimal refundAmount) throws IOException {
+ Assert.assertEquals(refundJsonCheck.getPaymentId(), paymentJsonSimple.getPaymentId());
+ Assert.assertEquals(refundJsonCheck.getRefundAmount().setScale(2, RoundingMode.HALF_UP), refundAmount.setScale(2, RoundingMode.HALF_UP));
Assert.assertEquals(refundJsonCheck.getEffectiveDate().getYear(), clock.getUTCNow().getYear());
Assert.assertEquals(refundJsonCheck.getEffectiveDate().getMonthOfYear(), clock.getUTCNow().getMonthOfYear());
Assert.assertEquals(refundJsonCheck.getEffectiveDate().getDayOfMonth(), clock.getUTCNow().getDayOfMonth());
@@ -60,7 +178,13 @@ public class TestPayment extends TestJaxrsBase {
Assert.assertEquals(refundJsonCheck.getRequestedDate().getDayOfMonth(), clock.getUTCNow().getDayOfMonth());
// Verify the refunds
- final List<RefundJson> retrievedRefunds = getRefundsForPayment(paymentId);
+ final List<RefundJson> retrievedRefunds = getRefundsForPayment(paymentJsonSimple.getPaymentId());
Assert.assertEquals(retrievedRefunds.size(), 1);
}
+
+ private void verifyInvoice(final PaymentJsonSimple paymentJsonSimple, final BigDecimal expectedInvoiceBalance) throws IOException {
+ final InvoiceJsonSimple invoiceJsonSimple = getInvoice(paymentJsonSimple.getInvoiceId());
+ Assert.assertEquals(invoiceJsonSimple.getBalance().setScale(2, BigDecimal.ROUND_HALF_UP),
+ expectedInvoiceBalance.setScale(2, BigDecimal.ROUND_HALF_UP));
+ }
}