Details
diff --git a/api/src/main/java/com/ning/billing/invoice/api/InvoicePaymentApi.java b/api/src/main/java/com/ning/billing/invoice/api/InvoicePaymentApi.java
index a95c98e..fa0e738 100644
--- a/api/src/main/java/com/ning/billing/invoice/api/InvoicePaymentApi.java
+++ b/api/src/main/java/com/ning/billing/invoice/api/InvoicePaymentApi.java
@@ -18,6 +18,7 @@ package com.ning.billing.invoice.api;
import java.math.BigDecimal;
import java.util.List;
+import java.util.Map;
import java.util.UUID;
import org.joda.time.DateTime;
@@ -26,6 +27,7 @@ import com.ning.billing.catalog.api.Currency;
import com.ning.billing.util.callcontext.CallContext;
public interface InvoicePaymentApi {
+
/**
* @param accountId id of the account
* @return All invoices, including migrated invoices
@@ -42,7 +44,21 @@ public interface InvoicePaymentApi {
public void notifyOfPayment(UUID invoiceId, BigDecimal amountOutstanding, Currency currency, UUID paymentId, DateTime paymentDate, CallContext context);
- public InvoicePayment createRefund(UUID paymentId, BigDecimal amount, boolean isInvoiceAdjusted, UUID paymentCookieId, CallContext context) throws InvoiceApiException;
+ /**
+ * Create a refund.
+ *
+ *
+ * @param paymentId payment associated with that refund
+ * @param amount amount to refund
+ * @param isInvoiceAdjusted whether the refund should trigger an invoice or invoice item adjustment
+ * @param invoiceItemIdsWithAmounts invoice item ids and associated amounts to adjust
+ * @param paymentCookieId payment cookie id
+ * @param context the call context
+ * @return the created invoice payment object associated with this refund
+ * @throws InvoiceApiException
+ */
+ public InvoicePayment createRefund(UUID paymentId, BigDecimal amount, boolean isInvoiceAdjusted, final Map<UUID, BigDecimal> invoiceItemIdsWithAmounts,
+ UUID paymentCookieId, CallContext context) throws InvoiceApiException;
public InvoicePayment createChargeback(UUID invoicePaymentId, BigDecimal amount, CallContext context) throws InvoiceApiException;
diff --git a/api/src/main/java/com/ning/billing/payment/api/PaymentApi.java b/api/src/main/java/com/ning/billing/payment/api/PaymentApi.java
index dc7488c..bf38550 100644
--- a/api/src/main/java/com/ning/billing/payment/api/PaymentApi.java
+++ b/api/src/main/java/com/ning/billing/payment/api/PaymentApi.java
@@ -18,6 +18,7 @@ package com.ning.billing.payment.api;
import java.math.BigDecimal;
import java.util.List;
+import java.util.Map;
import java.util.Set;
import java.util.UUID;
@@ -35,7 +36,58 @@ public interface PaymentApi {
public Refund getRefund(final UUID refundId)
throws PaymentApiException;
- public Refund createRefund(final Account account, final UUID paymentId, final BigDecimal refundAmount, final boolean isAdjusted, final CallContext context)
+ /**
+ * Create a refund for a given payment. The associated invoice is not adjusted.
+ *
+ * @param account account to refund
+ * @param paymentId payment associated with that refund
+ * @param refundAmount amount to refund
+ * @param context the call context
+ * @return the created Refund
+ * @throws PaymentApiException
+ */
+ public Refund createRefund(final Account account, final UUID paymentId, final BigDecimal refundAmount, final CallContext context)
+ throws PaymentApiException;
+
+ /**
+ * Create a refund for a given payment. The associated invoice is adjusted.
+ *
+ * @param account account to refund
+ * @param paymentId payment associated with that refund
+ * @param refundAmount amount to refund
+ * @param context the call context
+ * @return the created Refund
+ * @throws PaymentApiException
+ */
+ public Refund createRefundWithAdjustment(final Account account, final UUID paymentId, final BigDecimal refundAmount, final CallContext context)
+ throws PaymentApiException;
+
+ /**
+ * Create a refund for a given payment. The specified invoice items are fully adjusted.
+ * The refund amount will be the sum of all invoice items amounts.
+ *
+ * @param account account to refund
+ * @param paymentId payment associated with that refund
+ * @param invoiceItemIds invoice item ids to adjust
+ * @param context the call context
+ * @return the created Refund
+ * @throws PaymentApiException
+ */
+ public Refund createRefundWithItemsAdjustments(final Account account, final UUID paymentId, final Set<UUID> invoiceItemIds, final CallContext context)
+ throws PaymentApiException;
+
+ /**
+ * Create a refund for a given payment. The specified invoice items are partially adjusted.
+ * The refund amount will be the sum of all adjustments.
+ *
+ * @param account account to refund
+ * @param paymentId payment associated with that refund
+ * @param invoiceItemIdsWithAmounts invoice item ids and associated amounts to adjust
+ * @param context the call context
+ * @return the created Refund
+ * @throws PaymentApiException
+ */
+ public Refund createRefundWithItemsAdjustments(final Account account, final UUID paymentId, final Map<UUID, BigDecimal> invoiceItemIdsWithAmounts, final CallContext context)
throws PaymentApiException;
public List<Refund> getAccountRefunds(final Account account)
diff --git a/invoice/src/main/java/com/ning/billing/invoice/api/invoice/DefaultInvoicePaymentApi.java b/invoice/src/main/java/com/ning/billing/invoice/api/invoice/DefaultInvoicePaymentApi.java
index effca13..0bd3a5c 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/api/invoice/DefaultInvoicePaymentApi.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/api/invoice/DefaultInvoicePaymentApi.java
@@ -19,6 +19,7 @@ package com.ning.billing.invoice.api.invoice;
import java.math.BigDecimal;
import java.util.List;
+import java.util.Map;
import java.util.UUID;
import org.joda.time.DateTime;
@@ -108,7 +109,8 @@ public class DefaultInvoicePaymentApi implements InvoicePaymentApi {
@Override
public InvoicePayment createRefund(final UUID paymentId, final BigDecimal amount, final boolean isInvoiceAdjusted,
- final UUID paymentCookieId, final CallContext context) throws InvoiceApiException {
+ final Map<UUID, BigDecimal> invoiceItemIdsWithAmounts, final UUID paymentCookieId,
+ final CallContext context) throws InvoiceApiException {
return invoicePaymentWithException.executeAndThrow(new WithInvoiceApiExceptionCallback<InvoicePayment>() {
@@ -117,7 +119,7 @@ public class DefaultInvoicePaymentApi implements InvoicePaymentApi {
if (amount.compareTo(BigDecimal.ZERO) <= 0) {
throw new InvoiceApiException(ErrorCode.PAYMENT_REFUND_AMOUNT_NEGATIVE_OR_NULL);
}
- return dao.createRefund(paymentId, amount, isInvoiceAdjusted, paymentCookieId, context);
+ return dao.createRefund(paymentId, amount, isInvoiceAdjusted, invoiceItemIdsWithAmounts, paymentCookieId, context);
}
});
}
diff --git a/invoice/src/main/java/com/ning/billing/invoice/dao/DefaultInvoiceDao.java b/invoice/src/main/java/com/ning/billing/invoice/dao/DefaultInvoiceDao.java
index b3c81c9..a2f9a9b 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/dao/DefaultInvoiceDao.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/dao/DefaultInvoiceDao.java
@@ -20,6 +20,7 @@ import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
+import java.util.Map;
import java.util.UUID;
import javax.annotation.Nullable;
@@ -306,7 +307,9 @@ public class DefaultInvoiceDao implements InvoiceDao {
}
@Override
- public InvoicePayment createRefund(final UUID paymentId, final BigDecimal amount, final boolean isInvoiceAdjusted, final UUID paymentCookieId, final CallContext context)
+ public InvoicePayment createRefund(final UUID paymentId, final BigDecimal amount, final boolean isInvoiceAdjusted,
+ final Map<UUID, BigDecimal> invoiceItemIdsWithAmounts, final UUID paymentCookieId,
+ final CallContext context)
throws InvoiceApiException {
return invoicePaymentSqlDao.inTransaction(new Transaction<InvoicePayment, InvoicePaymentSqlDao>() {
@Override
@@ -324,9 +327,8 @@ public class DefaultInvoiceDao implements InvoiceDao {
throw new InvoiceApiException(ErrorCode.REFUND_AMOUNT_TOO_HIGH, requestedPositiveAmount, maxRefundAmount);
}
- // Before we go further, check if that refund already got inserted -- the payment system keps a state machine
+ // Before we go further, check if that refund already got inserted -- the payment system keeps a state machine
// and so this call may be called several time for the same paymentCookieId (which is really the refundId)
- //
final InvoicePayment existingRefund = transactional.getPaymentsForCookieId(paymentCookieId.toString());
if (existingRefund != null) {
return existingRefund;
@@ -443,8 +445,7 @@ public class DefaultInvoiceDao implements InvoiceDao {
@Override
public InvoiceItem insertCredit(final UUID accountId, final UUID invoiceId, final BigDecimal positiveCreditAmount,
- final LocalDate effectiveDate, final Currency currency,
- final CallContext context) {
+ final LocalDate effectiveDate, final Currency currency, final CallContext context) {
return invoiceSqlDao.inTransaction(new Transaction<InvoiceItem, InvoiceSqlDao>() {
@Override
public InvoiceItem inTransaction(final InvoiceSqlDao transactional, final TransactionStatus status) throws Exception {
@@ -495,7 +496,8 @@ public class DefaultInvoiceDao implements InvoiceDao {
* @return the adjustment item
*/
private InvoiceItem createAdjustmentItem(final InvoiceSqlDao transactional, final UUID invoiceId, final UUID invoiceItemId,
- final BigDecimal positiveAdjAmount, final Currency currency, final LocalDate effectiveDate) throws InvoiceApiException {// First, retrieve the invoice item in question
+ final BigDecimal positiveAdjAmount, final Currency currency, final LocalDate effectiveDate) throws InvoiceApiException {
+ // First, retrieve the invoice item in question
final InvoiceItemSqlDao invoiceItemSqlDao = transactional.become(InvoiceItemSqlDao.class);
final InvoiceItem invoiceItemToBeAdjusted = invoiceItemSqlDao.getById(invoiceItemId.toString());
if (invoiceItemToBeAdjusted == null) {
@@ -508,13 +510,13 @@ public class DefaultInvoiceDao implements InvoiceDao {
}
// Retrieve the amount and currency if needed
- final BigDecimal amountToRefund = Objects.firstNonNull(positiveAdjAmount, invoiceItemToBeAdjusted.getAmount());
+ final BigDecimal amountToAdjust = Objects.firstNonNull(positiveAdjAmount, invoiceItemToBeAdjusted.getAmount());
// TODO - should we enforce the currency (and respect the original one) here if the amount passed was null?
final Currency currencyForAdjustment = Objects.firstNonNull(currency, invoiceItemToBeAdjusted.getCurrency());
// Finally, create the adjustment
// Note! The amount is negated here!
- return new ItemAdjInvoiceItem(invoiceItemToBeAdjusted, effectiveDate, amountToRefund.negate(), currencyForAdjustment);
+ return new ItemAdjInvoiceItem(invoiceItemToBeAdjusted, effectiveDate, amountToAdjust.negate(), currencyForAdjustment);
}
/**
diff --git a/invoice/src/main/java/com/ning/billing/invoice/dao/InvoiceDao.java b/invoice/src/main/java/com/ning/billing/invoice/dao/InvoiceDao.java
index 7c93aba..a9104f9 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/dao/InvoiceDao.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/dao/InvoiceDao.java
@@ -18,6 +18,7 @@ package com.ning.billing.invoice.dao;
import java.math.BigDecimal;
import java.util.List;
+import java.util.Map;
import java.util.UUID;
import javax.annotation.Nullable;
@@ -70,7 +71,21 @@ public interface InvoiceDao {
InvoicePayment postChargeback(final UUID invoicePaymentId, final BigDecimal amount, final CallContext context) throws InvoiceApiException;
- InvoicePayment createRefund(UUID paymentId, BigDecimal amount, boolean isInvoiceAdjusted, UUID paymentCookieId, CallContext context) throws InvoiceApiException;
+ /**
+ * Create a refund.
+ *
+ *
+ * @param paymentId payment associated with that refund
+ * @param amount amount to refund
+ * @param isInvoiceAdjusted whether the refund should trigger an invoice or invoice item adjustment
+ * @param invoiceItemIdsWithAmounts invoice item ids and associated amounts to adjust
+ * @param paymentCookieId payment cookie id
+ * @param context the call context
+ * @return the created invoice payment object associated with this refund
+ * @throws InvoiceApiException
+ */
+ InvoicePayment createRefund(final UUID paymentId, final BigDecimal amount, final boolean isInvoiceAdjusted, final Map<UUID, BigDecimal> invoiceItemIdsWithAmounts,
+ final UUID paymentCookieId, final CallContext context) throws InvoiceApiException;
BigDecimal getRemainingAmountPaid(final UUID invoicePaymentId);
diff --git a/invoice/src/test/java/com/ning/billing/invoice/api/MockInvoicePaymentApi.java b/invoice/src/test/java/com/ning/billing/invoice/api/MockInvoicePaymentApi.java
index 87183b4..f229e4a 100644
--- a/invoice/src/test/java/com/ning/billing/invoice/api/MockInvoicePaymentApi.java
+++ b/invoice/src/test/java/com/ning/billing/invoice/api/MockInvoicePaymentApi.java
@@ -19,6 +19,7 @@ package com.ning.billing.invoice.api;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
+import java.util.Map;
import java.util.UUID;
import java.util.concurrent.CopyOnWriteArrayList;
@@ -167,8 +168,9 @@ public class MockInvoicePaymentApi implements InvoicePaymentApi {
}
@Override
- public InvoicePayment createRefund(UUID paymentId,
- BigDecimal amount, boolean isInvoiceAdjusted, UUID paymentCookieId, CallContext context)
+ public InvoicePayment createRefund(final UUID paymentId, final BigDecimal amount, final boolean isInvoiceAdjusted,
+ final Map<UUID, BigDecimal> invoiceItemIdsWithAmounts, final UUID paymentCookieId,
+ final CallContext context)
throws InvoiceApiException {
return null;
}
diff --git a/invoice/src/test/java/com/ning/billing/invoice/dao/MockInvoiceDao.java b/invoice/src/test/java/com/ning/billing/invoice/dao/MockInvoiceDao.java
index 1a26152..301866c 100644
--- a/invoice/src/test/java/com/ning/billing/invoice/dao/MockInvoiceDao.java
+++ b/invoice/src/test/java/com/ning/billing/invoice/dao/MockInvoiceDao.java
@@ -25,12 +25,10 @@ import java.util.UUID;
import javax.annotation.Nullable;
-import org.joda.time.DateTime;
import org.joda.time.LocalDate;
import com.google.inject.Inject;
-import com.ning.billing.account.api.BillCycleDay;
import com.ning.billing.catalog.api.Currency;
import com.ning.billing.invoice.api.Invoice;
import com.ning.billing.invoice.api.InvoiceApiException;
@@ -282,10 +280,10 @@ public class MockInvoiceDao implements InvoiceDao {
}
@Override
- public InvoicePayment createRefund(UUID paymentId,
- BigDecimal amount, boolean isInvoiceAdjusted, UUID paymentCookieId, CallContext context)
+ public InvoicePayment createRefund(final UUID paymentId, final BigDecimal amount, final boolean isInvoiceAdjusted,
+ final Map<UUID, BigDecimal> invoiceItemIdsWithAmounts, final UUID paymentCookieId,
+ final CallContext context)
throws InvoiceApiException {
return null;
}
-
}
diff --git a/invoice/src/test/java/com/ning/billing/invoice/dao/TestInvoiceDao.java b/invoice/src/test/java/com/ning/billing/invoice/dao/TestInvoiceDao.java
index ae0198b..55a2e61 100644
--- a/invoice/src/test/java/com/ning/billing/invoice/dao/TestInvoiceDao.java
+++ b/invoice/src/test/java/com/ning/billing/invoice/dao/TestInvoiceDao.java
@@ -27,7 +27,6 @@ import org.joda.time.DateTime;
import org.joda.time.DateTimeZone;
import org.joda.time.LocalDate;
import org.mockito.Mockito;
-import org.testng.Assert;
import org.testng.annotations.Test;
import com.ning.billing.catalog.DefaultPrice;
@@ -66,6 +65,8 @@ import com.ning.billing.util.tag.Tag;
import com.ning.billing.util.tag.dao.AuditedTagDao;
import com.ning.billing.util.tag.dao.TagDao;
+import com.google.common.collect.ImmutableMap;
+
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertNotNull;
import static org.testng.Assert.assertNull;
@@ -550,7 +551,7 @@ public class TestInvoiceDao extends InvoiceDaoTestBase {
balance = invoiceDao.getAccountBalance(accountId);
assertEquals(balance.compareTo(new BigDecimal("0.00")), 0);
- invoiceDao.createRefund(paymentId, refund1, withAdjustment, UUID.randomUUID(), context);
+ invoiceDao.createRefund(paymentId, refund1, withAdjustment, ImmutableMap.<UUID, BigDecimal>of(), UUID.randomUUID(), context);
balance = invoiceDao.getAccountBalance(accountId);
if (withAdjustment) {
assertEquals(balance.compareTo(BigDecimal.ZERO), 0);
@@ -642,7 +643,7 @@ public class TestInvoiceDao extends InvoiceDaoTestBase {
assertEquals(cba.compareTo(new BigDecimal("10.00")), 0);
// PARTIAL REFUND on the payment
- invoiceDao.createRefund(paymentId, refundAmount, withAdjustment, UUID.randomUUID(), context);
+ invoiceDao.createRefund(paymentId, refundAmount, withAdjustment, ImmutableMap.<UUID, BigDecimal>of(), UUID.randomUUID(), context);
balance = invoiceDao.getAccountBalance(accountId);
assertEquals(balance.compareTo(expectedFinalBalance), 0);
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 8e61d24..87fcff3 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
@@ -110,7 +110,12 @@ public class PaymentResource extends JaxRsResourceBase {
final Payment payment = paymentApi.getPayment(paymentUuid);
final Account account = accountApi.getAccountById(payment.getAccountId());
- final Refund result = paymentApi.createRefund(account, paymentUuid, json.getRefundAmount(), json.isAdjusted(), context.createContext(createdBy, reason, comment));
+ final Refund result;
+ if (json.isAdjusted()) {
+ result = paymentApi.createRefundWithAdjustment(account, paymentUuid, json.getRefundAmount(), context.createContext(createdBy, reason, comment));
+ } else {
+ result = paymentApi.createRefund(account, paymentUuid, json.getRefundAmount(), context.createContext(createdBy, reason, comment));
+ }
return uriBuilder.buildResponse(RefundResource.class, "getRefund", result.getId(), uriInfo.getBaseUri().toString());
}
diff --git a/payment/src/main/java/com/ning/billing/payment/api/DefaultPaymentApi.java b/payment/src/main/java/com/ning/billing/payment/api/DefaultPaymentApi.java
index b4631a2..a2c1f11 100644
--- a/payment/src/main/java/com/ning/billing/payment/api/DefaultPaymentApi.java
+++ b/payment/src/main/java/com/ning/billing/payment/api/DefaultPaymentApi.java
@@ -17,7 +17,9 @@
package com.ning.billing.payment.api;
import java.math.BigDecimal;
+import java.util.HashMap;
import java.util.List;
+import java.util.Map;
import java.util.Set;
import java.util.UUID;
@@ -28,6 +30,7 @@ import com.ning.billing.payment.core.PaymentProcessor;
import com.ning.billing.payment.core.RefundProcessor;
import com.ning.billing.util.callcontext.CallContext;
+import com.google.common.collect.ImmutableMap;
import com.google.inject.Inject;
public class DefaultPaymentApi implements PaymentApi {
@@ -82,14 +85,34 @@ public class DefaultPaymentApi implements PaymentApi {
}
@Override
- public Refund createRefund(final Account account, final UUID paymentId,
- final BigDecimal refundAmount, final boolean isAdjusted, final CallContext context)
- throws PaymentApiException {
+ public Refund createRefund(final Account account, final UUID paymentId, final BigDecimal refundAmount, final CallContext context) throws PaymentApiException {
+ if (refundAmount == null || refundAmount.compareTo(BigDecimal.ZERO) <= 0) {
+ throw new PaymentApiException(ErrorCode.PAYMENT_REFUND_AMOUNT_NEGATIVE_OR_NULL);
+ }
+ return refundProcessor.createRefund(account, paymentId, refundAmount, false, ImmutableMap.<UUID, BigDecimal>of(), context);
+ }
+
+ @Override
+ public Refund createRefundWithAdjustment(final Account account, final UUID paymentId, final BigDecimal refundAmount, final CallContext context) throws PaymentApiException {
if (refundAmount == null || refundAmount.compareTo(BigDecimal.ZERO) <= 0) {
throw new PaymentApiException(ErrorCode.PAYMENT_REFUND_AMOUNT_NEGATIVE_OR_NULL);
}
- return refundProcessor.createRefund(account, paymentId, refundAmount, isAdjusted, context);
+ return refundProcessor.createRefund(account, paymentId, refundAmount, true, ImmutableMap.<UUID, BigDecimal>of(), context);
+ }
+
+ @Override
+ public Refund createRefundWithItemsAdjustments(final Account account, final UUID paymentId, final Set<UUID> invoiceItemIds, final CallContext context) throws PaymentApiException {
+ final Map<UUID, BigDecimal> invoiceItemIdsWithAmounts = new HashMap<UUID, BigDecimal>();
+ for (final UUID invoiceItemId : invoiceItemIds) {
+ invoiceItemIdsWithAmounts.put(invoiceItemId, null);
+ }
+ return refundProcessor.createRefund(account, paymentId, null, true, invoiceItemIdsWithAmounts, context);
+ }
+
+ @Override
+ public Refund createRefundWithItemsAdjustments(final Account account, final UUID paymentId, final Map<UUID, BigDecimal> invoiceItemIdsWithAmounts, final CallContext context) throws PaymentApiException {
+ return refundProcessor.createRefund(account, paymentId, null, true, invoiceItemIdsWithAmounts, context);
}
@Override
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 b354c70..ff4ed16 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
@@ -21,9 +21,11 @@ import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
+import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ExecutorService;
+import javax.annotation.Nullable;
import javax.inject.Inject;
import org.slf4j.Logger;
@@ -53,8 +55,10 @@ import com.ning.billing.util.callcontext.UserType;
import com.ning.billing.util.globallocker.GlobalLocker;
import com.google.common.base.Function;
+import com.google.common.base.Objects;
import com.google.common.base.Predicate;
import com.google.common.collect.Collections2;
+import com.google.common.collect.ImmutableMap;
import com.google.inject.name.Named;
import static com.ning.billing.payment.glue.PaymentModule.PLUGIN_EXECUTOR_NAMED;
@@ -80,13 +84,29 @@ public class RefundProcessor extends ProcessorBase {
this.factory = factory;
}
- public Refund createRefund(final Account account, final UUID paymentId, final BigDecimal refundAmount, final boolean isAdjusted, final CallContext context)
+ /**
+ * 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 invoiceItemIdsWithAmounts invoice item ids and associated amounts to adjust
+ * @param context the call context
+ * @return the created context
+ * @throws PaymentApiException
+ */
+ public Refund createRefund(final Account account, final UUID paymentId, @Nullable final BigDecimal specifiedRefundAmount,
+ final boolean isAdjusted, final Map<UUID, BigDecimal> invoiceItemIdsWithAmounts, final CallContext context)
throws PaymentApiException {
return new WithAccountLock<Refund>().processAccountWithLock(locker, account.getExternalKey(), new WithAccountLockCallback<Refund>() {
@Override
public Refund doOperation() throws PaymentApiException {
+ // First, compute the refund amount, if necessary
+ final BigDecimal refundAmount = computeRefundAmount(specifiedRefundAmount, invoiceItemIdsWithAmounts);
+
try {
final PaymentModelDao payment = paymentDao.getPayment(paymentId);
@@ -142,7 +162,7 @@ public class RefundProcessor extends ProcessorBase {
}
paymentDao.updateRefundStatus(refundInfo.getId(), RefundStatus.PLUGIN_COMPLETED, context);
- invoicePaymentApi.createRefund(paymentId, refundAmount, isAdjusted, refundInfo.getId(), context);
+ invoicePaymentApi.createRefund(paymentId, refundAmount, isAdjusted, invoiceItemIdsWithAmounts, refundInfo.getId(), context);
paymentDao.updateRefundStatus(refundInfo.getId(), RefundStatus.COMPLETED, context);
@@ -157,6 +177,27 @@ public class RefundProcessor extends ProcessorBase {
});
}
+ /**
+ * Compute the refund amount (computed from the invoice or invoice items as necessary).
+ *
+ * @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) {
+ BigDecimal amountFromItems = BigDecimal.ZERO;
+ for (final BigDecimal itemAmount : invoiceItemIdsWithAmounts.values()) {
+ amountFromItems = amountFromItems.add(itemAmount);
+ }
+
+ // Sanity check: if some items were specified, then the sum should be equal to specified refund amount, if specified
+ if (amountFromItems.compareTo(BigDecimal.ZERO) != 0 && specifiedRefundAmount != null && specifiedRefundAmount.compareTo(amountFromItems) != 0) {
+ throw new IllegalArgumentException("You can't specify a refund amount that doesn't match the invoice items amounts");
+ }
+
+ return Objects.firstNonNull(specifiedRefundAmount, amountFromItems);
+ }
+
public Refund getRefund(final UUID refundId)
throws PaymentApiException {
RefundModelDao result = paymentDao.getRefund(refundId);
@@ -235,7 +276,8 @@ public class RefundProcessor extends ProcessorBase {
try {
final CallContext context = factory.createCallContext("RefundProcessor", CallOrigin.INTERNAL, UserType.SYSTEM);
for (final RefundModelDao cur : refundsToBeFixed) {
- invoicePaymentApi.createRefund(cur.getPaymentId(), cur.getAmount(), cur.isAdjsuted(), cur.getId(), context);
+ // TODO - we currently don't save the items to be adjusted. If we crash, they won't be adjusted...
+ invoicePaymentApi.createRefund(cur.getPaymentId(), cur.getAmount(), cur.isAdjsuted(), ImmutableMap.<UUID, BigDecimal>of(), cur.getId(), context);
paymentDao.updateRefundStatus(cur.getId(), RefundStatus.COMPLETED, context);
}
} catch (InvoiceApiException e) {