killbill-aplcache

#255 - external refund implementation by credit. New tests

8/24/2016 5:22:55 PM

Details

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 f2f1a01..8d4c3e1 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,7 +19,9 @@
 package org.killbill.billing.jaxrs.resources;
 
 import java.math.BigDecimal;
+import java.util.Collection;
 import java.util.HashMap;
+import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
 import java.util.UUID;
@@ -55,6 +57,7 @@ import org.killbill.billing.jaxrs.util.JaxrsUriBuilder;
 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.PaymentMethod;
 import org.killbill.billing.payment.api.PluginProperty;
 import org.killbill.billing.util.UUIDs;
 import org.killbill.billing.util.api.AuditUserApi;
@@ -180,11 +183,24 @@ public class InvoicePaymentResource extends JaxRsResourceBase {
         }
 
         final Payment result;
-        if (externalPayment) {
+        PaymentMethod paymentMethod = null;
+        try {
+            paymentMethod = paymentApi.getPaymentMethodById(payment.getPaymentMethodId(), false, false, pluginProperties, callContext);
+        } catch (PaymentApiException e) {
+            log.warn("Payment method {} does not found", payment.getPaymentMethodId());
+        }
+
+        if (externalPayment && (paymentMethod == null)) {
+            // TODO to complete when a different PM is passed
+            UUID externalPaymentMethodId = null;
+
+            final Collection<PluginProperty> pluginPropertiesForExternalRefund = new LinkedList<PluginProperty>();
+            Iterables.addAll(pluginPropertiesForExternalRefund, pluginProperties);
+            pluginPropertiesForExternalRefund.add(new PluginProperty("IPCD_PAYMENT_ID", paymentUuid, false));
 
-            result = paymentApi.createCreditWithPaymentControl(account, null, null, json.getAmount(), account.getCurrency(),
-                                                               paymentExternalKey, transactionExternalKey, pluginProperties,
-                                                               createInvoicePaymentControlPluginApiPaymentOptions(false), callContext);
+            result = paymentApi.createCreditWithPaymentControl(account, externalPaymentMethodId, null, json.getAmount(), account.getCurrency(),
+                                                               paymentExternalKey, transactionExternalKey, pluginPropertiesForExternalRefund,
+                                                               createInvoicePaymentControlPluginApiPaymentOptions(true), callContext);
         } else {
             result = paymentApi.createRefundWithPaymentControl(account, payment.getId(), json.getAmount(), account.getCurrency(), transactionExternalKey,
                                                                pluginProperties, createInvoicePaymentControlPluginApiPaymentOptions(false), callContext);
diff --git a/payment/src/main/java/org/killbill/billing/payment/invoice/InvoicePaymentControlPluginApi.java b/payment/src/main/java/org/killbill/billing/payment/invoice/InvoicePaymentControlPluginApi.java
index e9441b1..b8bc30f 100644
--- a/payment/src/main/java/org/killbill/billing/payment/invoice/InvoicePaymentControlPluginApi.java
+++ b/payment/src/main/java/org/killbill/billing/payment/invoice/InvoicePaymentControlPluginApi.java
@@ -93,6 +93,7 @@ public final class InvoicePaymentControlPluginApi implements PaymentControlPlugi
     public static final String PROP_IPCD_INVOICE_ID = "IPCD_INVOICE_ID";
     public static final String PROP_IPCD_REFUND_IDS_WITH_AMOUNT_KEY = "IPCD_REFUND_IDS_AMOUNTS";
     public static final String PROP_IPCD_REFUND_WITH_ADJUSTMENTS = "IPCD_REFUND_WITH_ADJUSTMENTS";
+    public static final String PROP_IPCD_PAYMENT_ID = "IPCD_PAYMENT_ID";
 
     private final PaymentConfig paymentConfig;
     private final InvoiceInternalApi invoiceApi;
@@ -221,7 +222,14 @@ public final class InvoicePaymentControlPluginApi implements PaymentControlPlugi
                     break;
 
                 case CREDIT:
-                    // TODO implement
+                    final Map<UUID, BigDecimal> idWithAmountMap = extractIdsWithAmountFromProperties(pluginProperties);
+                    final PluginProperty properties = getPluginProperty(pluginProperties, PROP_IPCD_REFUND_WITH_ADJUSTMENTS);
+                    final boolean isInvoiceAdjusted = properties != null ? Boolean.valueOf((String) properties.getValue()) : false;
+
+                    final PluginProperty legacyPayment = getPluginProperty(pluginProperties, PROP_IPCD_PAYMENT_ID);
+                    final UUID paymentId = legacyPayment != null ? (UUID) legacyPayment.getValue() : paymentControlContext.getPaymentId();
+
+                    invoiceApi.recordRefund(paymentId, paymentControlContext.getAmount(), isInvoiceAdjusted, idWithAmountMap, paymentControlContext.getTransactionExternalKey(), internalContext);
                     break;
 
                 default:
@@ -261,6 +269,7 @@ public final class InvoicePaymentControlPluginApi implements PaymentControlPlugi
 
                 nextRetryDate = computeNextRetryDate(paymentControlContext.getPaymentExternalKey(), paymentControlContext.isApiPayment(), internalContext);
                 break;
+            case CREDIT:
             case REFUND:
                 // We don't retry REFUND
                 break;
diff --git a/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestExternalRefund.java b/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestExternalRefund.java
index e364682..fb32972 100644
--- a/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestExternalRefund.java
+++ b/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestExternalRefund.java
@@ -19,23 +19,81 @@ package org.killbill.billing.jaxrs;
 
 import java.math.BigDecimal;
 import java.util.List;
+import java.util.UUID;
 
 import org.joda.time.DateTime;
+import org.killbill.billing.client.KillBillClientException;
 import org.killbill.billing.client.model.Account;
 import org.killbill.billing.client.model.Invoice;
 import org.killbill.billing.client.model.InvoiceItem;
 import org.killbill.billing.client.model.InvoicePayment;
 import org.killbill.billing.client.model.InvoicePaymentTransaction;
+import org.killbill.billing.client.model.InvoicePayments;
 import org.killbill.billing.client.model.Invoices;
 import org.killbill.billing.client.model.Payment;
 import org.killbill.billing.client.model.Payments;
+import org.killbill.billing.invoice.api.InvoiceItemType;
+import org.killbill.billing.payment.api.TransactionType;
 import org.testng.annotations.Test;
 
 import static org.testng.Assert.assertEquals;
 import static org.testng.Assert.assertNotNull;
+import static org.testng.Assert.assertTrue;
 
 public class TestExternalRefund extends TestJaxrsBase {
 
+    @Test(groups = "slow", description = "#255 - Scenario 0 - Can refund an automatic payment. This is a test to validate the correct behaviour.")
+    public void testAutomaticPaymentAndRefund() throws Exception {
+        final DateTime initialDate = new DateTime(2012, 4, 25, 0, 3, 42, 0);
+        clock.setDeltaFromReality(initialDate.getMillis() - clock.getUTCNow().getMillis());
+
+        final Account accountJson = createAccountWithPMBundleAndSubscriptionAndWaitForFirstInvoice();
+        final Payments paymentsForAccount = killBillClient.getPaymentsForAccount(accountJson.getAccountId(), requestOptions);
+        final Payment payment = paymentsForAccount.get(paymentsForAccount.size() - 1);
+
+        Invoices invoices = killBillClient.getInvoicesForAccount(accountJson.getAccountId(), true, true, requestOptions);
+        final List<InvoiceItem> itemsToBeAdjusted = invoices.get(1).getItems();
+
+        // regular refund
+        final InvoicePaymentTransaction invoicePaymentTransactionRequest = new InvoicePaymentTransaction();
+        invoicePaymentTransactionRequest.setAmount(BigDecimal.valueOf(249.95));
+        invoicePaymentTransactionRequest.setCurrency(accountJson.getCurrency().toString());
+        invoicePaymentTransactionRequest.setPaymentId(payment.getPaymentId());
+        final InvoicePayment invoicePaymentRefund = killBillClient.createInvoicePaymentRefund(invoicePaymentTransactionRequest, requestOptions);
+        assertNotNull(invoicePaymentRefund);
+
+        assertSingleInvoicePaymentRefund(invoicePaymentRefund);
+        assertRefundInvoiceNoAdjustments(accountJson.getAccountId());
+        assertRefundAccountBalance(accountJson.getAccountId(), BigDecimal.valueOf(249.95), BigDecimal.ZERO);
+    }
+
+    @Test(groups = "slow", description = "#255 - Scenario 0 - Can refund an automatic payment over item adjustments. This is a test to validate the correct behaviour.")
+    public void testAutomaticPaymentAndRefundWithAdjustments() throws Exception {
+        final DateTime initialDate = new DateTime(2012, 4, 25, 0, 3, 42, 0);
+        clock.setDeltaFromReality(initialDate.getMillis() - clock.getUTCNow().getMillis());
+
+        final Account accountJson = createAccountWithPMBundleAndSubscriptionAndWaitForFirstInvoice();
+        final Payments paymentsForAccount = killBillClient.getPaymentsForAccount(accountJson.getAccountId(), requestOptions);
+        final Payment payment = paymentsForAccount.get(paymentsForAccount.size() - 1);
+
+        Invoices invoices = killBillClient.getInvoicesForAccount(accountJson.getAccountId(), true, true, requestOptions);
+        final List<InvoiceItem> itemsToBeAdjusted = invoices.get(1).getItems();
+
+        // regular refund
+        final InvoicePaymentTransaction invoicePaymentTransactionRequest = new InvoicePaymentTransaction();
+        invoicePaymentTransactionRequest.setAmount(BigDecimal.valueOf(249.95));
+        invoicePaymentTransactionRequest.setCurrency(accountJson.getCurrency().toString());
+        invoicePaymentTransactionRequest.setPaymentId(payment.getPaymentId());
+        invoicePaymentTransactionRequest.setIsAdjusted(true);
+        invoicePaymentTransactionRequest.setAdjustments(itemsToBeAdjusted);
+        final InvoicePayment invoicePaymentRefund = killBillClient.createInvoicePaymentRefund(invoicePaymentTransactionRequest, requestOptions);
+        assertNotNull(invoicePaymentRefund);
+
+        assertSingleInvoicePaymentRefund(invoicePaymentRefund);
+        assertRefundInvoiceAdjustments(accountJson.getAccountId());
+        assertRefundAccountBalance(accountJson.getAccountId(), BigDecimal.ZERO, BigDecimal.ZERO);
+    }
+
     @Test(groups = "slow", description = "#255 - Scenario 1 - Can refund a manual payment though an external refund")
     public void testManualPaymentAndExternalRefund() throws Exception {
         final DateTime initialDate = new DateTime(2012, 4, 25, 0, 3, 42, 0);
@@ -62,11 +120,48 @@ public class TestExternalRefund extends TestJaxrsBase {
         final InvoicePaymentTransaction invoicePaymentTransactionRequest = new InvoicePaymentTransaction();
         invoicePaymentTransactionRequest.setAmount(BigDecimal.valueOf(249.95));
         invoicePaymentTransactionRequest.setPaymentId(invoicePayment.getPaymentId());
-        final InvoicePayment invoicePaymentRefund = killBillClient.createInvoicePaymentRefund(invoicePaymentTransactionRequest, requestOptions);
+        final InvoicePayment invoicePaymentRefund = killBillClient.createInvoicePaymentRefund(invoicePaymentTransactionRequest, true, requestOptions);
+        assertNotNull(invoicePaymentRefund);
+
+        assertSingleInvoicePaymentRefund(invoicePaymentRefund);
+        assertRefundInvoiceNoAdjustments(accountJson.getAccountId());
+        assertRefundAccountBalance(accountJson.getAccountId(), BigDecimal.valueOf(249.95), BigDecimal.ZERO);
+    }
+
+    @Test(groups = "slow", description = "#255 - Scenario 1 - Can refund a manual payment though an external refund over item adjustments")
+    public void testManualPaymentAndExternalRefundWithAdjustments() throws Exception {
+        final DateTime initialDate = new DateTime(2012, 4, 25, 0, 3, 42, 0);
+        clock.setDeltaFromReality(initialDate.getMillis() - clock.getUTCNow().getMillis());
 
+        final Account accountJson = createAccountWithExternalPMBundleAndSubscriptionAndManualPayTagAndWaitForFirstInvoice();
+
+        final Invoices invoicesForAccount = killBillClient.getInvoicesForAccount(accountJson.getAccountId(), requestOptions);
+        final Invoice unpaidInvoice = invoicesForAccount.get(1);
+        assertEquals(unpaidInvoice.getBalance().compareTo(BigDecimal.valueOf(249.95)), 0);
+
+        final Payments paymentsForAccount = killBillClient.getPaymentsForAccount(accountJson.getAccountId(), requestOptions);
+        assertEquals(paymentsForAccount.size(), 0);
+
+        final InvoicePayment invoicePaymentRequest = new InvoicePayment();
+        invoicePaymentRequest.setTargetInvoiceId(unpaidInvoice.getInvoiceId());
+        invoicePaymentRequest.setAccountId(accountJson.getAccountId());
+        invoicePaymentRequest.setCurrency(unpaidInvoice.getCurrency().toString());
+        invoicePaymentRequest.setPurchasedAmount(unpaidInvoice.getAmount());
+        final InvoicePayment invoicePayment = killBillClient.createInvoicePayment(invoicePaymentRequest, true, requestOptions);
+        assertEquals(invoicePayment.getPurchasedAmount().compareTo(BigDecimal.valueOf(249.95)), 0);
+        assertEquals(invoicePayment.getRefundedAmount().compareTo(BigDecimal.ZERO), 0);
+
+        final InvoicePaymentTransaction invoicePaymentTransactionRequest = new InvoicePaymentTransaction();
+        invoicePaymentTransactionRequest.setAmount(BigDecimal.valueOf(249.95));
+        invoicePaymentTransactionRequest.setPaymentId(invoicePayment.getPaymentId());
+        invoicePaymentTransactionRequest.setIsAdjusted(true);
+        invoicePaymentTransactionRequest.setAdjustments(unpaidInvoice.getItems());
+        final InvoicePayment invoicePaymentRefund = killBillClient.createInvoicePaymentRefund(invoicePaymentTransactionRequest, requestOptions);
         assertNotNull(invoicePaymentRefund);
-        assertEquals(invoicePaymentRefund.getPurchasedAmount().compareTo(BigDecimal.valueOf(249.95)), 0);
-        assertEquals(invoicePaymentRefund.getRefundedAmount().compareTo(BigDecimal.valueOf(249.95)), 0);
+
+        assertSingleInvoicePaymentRefund(invoicePaymentRefund);
+        assertRefundInvoiceAdjustments(accountJson.getAccountId());
+        assertRefundAccountBalance(accountJson.getAccountId(), BigDecimal.ZERO, BigDecimal.ZERO);
     }
 
     @Test(groups = "slow", description = "#255 - Scenario 2a - Can refund an automatic payment though an external refund")
@@ -85,12 +180,12 @@ public class TestExternalRefund extends TestJaxrsBase {
         invoicePaymentTransactionRequest.setAmount(BigDecimal.valueOf(249.95));
         invoicePaymentTransactionRequest.setCurrency(accountJson.getCurrency().toString());
         invoicePaymentTransactionRequest.setPaymentId(payment.getPaymentId());
-        final InvoicePayment invoicePaymentRefund = killBillClient.createInvoicePaymentRefund(invoicePaymentTransactionRequest, true, requestOptions);
-        assertNotNull(invoicePaymentRefund);
-        assertEquals(invoicePaymentRefund.getCreditedAmount().compareTo(BigDecimal.valueOf(249.95)), 0);
+        final InvoicePayment invoicePaymentExternalRefund = killBillClient.createInvoicePaymentRefund(invoicePaymentTransactionRequest, true, requestOptions);
+        assertNotNull(invoicePaymentExternalRefund);
 
-        // TODO check account balance
-        // TODO check invoice items
+        assertInvoicePaymentsExternalRefund(accountJson.getAccountId(), invoicePaymentExternalRefund);
+        assertRefundInvoiceNoAdjustments(accountJson.getAccountId());
+        assertRefundAccountBalance(accountJson.getAccountId(), BigDecimal.valueOf(249.95), BigDecimal.ZERO);
 
     }
 
@@ -115,16 +210,16 @@ public class TestExternalRefund extends TestJaxrsBase {
         invoicePaymentTransactionRequest.setPaymentId(payment.getPaymentId());
         invoicePaymentTransactionRequest.setIsAdjusted(true);
         invoicePaymentTransactionRequest.setAdjustments(itemsToBeAdjusted);
-        final InvoicePayment invoicePaymentRefund = killBillClient.createInvoicePaymentRefund(invoicePaymentTransactionRequest, true, requestOptions);
-        assertNotNull(invoicePaymentRefund);
-        assertEquals(invoicePaymentRefund.getCreditedAmount().compareTo(BigDecimal.valueOf(249.95)), 0);
+        final InvoicePayment invoicePaymentExternalRefund = killBillClient.createInvoicePaymentRefund(invoicePaymentTransactionRequest, true, requestOptions);
+        assertNotNull(invoicePaymentExternalRefund);
 
-        // TODO check account balance
-        // TODO check invoice items
+        assertInvoicePaymentsExternalRefund(accountJson.getAccountId(), invoicePaymentExternalRefund);
+        assertRefundInvoiceAdjustments(accountJson.getAccountId());
+        assertRefundAccountBalance(accountJson.getAccountId(), BigDecimal.ZERO, BigDecimal.ZERO);
 
     }
 
-    @Test(groups = "slow", description = "#255 - Scenario 2b - Can refund an automatic payment though an external refund")
+    @Test(groups = "slow", description = "#255 - Scenario 2b - Can refund an automatic payment though another existing payment method")
     public void testAutomaticPaymentAndRefundWithDifferentPM() throws Exception {
         final DateTime initialDate = new DateTime(2012, 4, 25, 0, 3, 42, 0);
         clock.setDeltaFromReality(initialDate.getMillis() - clock.getUTCNow().getMillis());
@@ -135,4 +230,67 @@ public class TestExternalRefund extends TestJaxrsBase {
 
     }
 
+    private void assertRefundInvoiceAdjustments(final UUID accountId) throws KillBillClientException {
+        final Invoices invoices;
+        invoices = killBillClient.getInvoicesForAccount(accountId, true, true, requestOptions);
+        final Invoice invoiceWithRefund = invoices.get(1);
+        assertEquals(invoiceWithRefund.getAmount().compareTo(BigDecimal.ZERO), 0);
+        assertEquals(invoiceWithRefund.getRefundAdj().compareTo(BigDecimal.valueOf(249.95).negate()), 0);
+        assertEquals(invoiceWithRefund.getItems().size(), 2);
+        assertEquals(invoiceWithRefund.getItems().get(0).getItemType(), InvoiceItemType.RECURRING.toString());
+        assertEquals(invoiceWithRefund.getItems().get(0).getAmount().compareTo(BigDecimal.valueOf(249.95)), 0);
+        assertEquals(invoiceWithRefund.getItems().get(1).getItemType(), InvoiceItemType.ITEM_ADJ.toString());
+        assertEquals(invoiceWithRefund.getItems().get(1).getAmount().compareTo(BigDecimal.valueOf(249.95).negate()), 0);
+    }
+
+    private void assertRefundInvoiceNoAdjustments(final UUID accountId) throws KillBillClientException {
+        final Invoices invoices = killBillClient.getInvoicesForAccount(accountId, true, true, requestOptions);
+        final Invoice invoiceWithRefund = invoices.get(1);
+        assertEquals(invoiceWithRefund.getAmount().compareTo(BigDecimal.valueOf(249.95)), 0);
+        assertEquals(invoiceWithRefund.getRefundAdj().compareTo(BigDecimal.valueOf(249.95).negate()), 0);
+        assertEquals(invoiceWithRefund.getItems().size(), 1);
+        assertEquals(invoiceWithRefund.getItems().get(0).getItemType(), InvoiceItemType.RECURRING.toString());
+        assertEquals(invoiceWithRefund.getItems().get(0).getAmount().compareTo(BigDecimal.valueOf(249.95)), 0);
+    }
+
+    private void assertRefundAccountBalance(final UUID accountId, final BigDecimal balanceAmount, final BigDecimal cbaAmount) throws KillBillClientException {
+        final Account account = killBillClient.getAccount(accountId, true, true, requestOptions);
+        assertEquals(account.getAccountBalance().compareTo(balanceAmount), 0);
+        assertEquals(account.getAccountCBA().compareTo(cbaAmount), 0);
+    }
+
+    private void assertInvoicePaymentsExternalRefund(final UUID accountId, final InvoicePayment invoicePaymentExternalRefund) throws KillBillClientException {
+        final InvoicePayments invoicePaymentsForAccount = killBillClient.getInvoicePaymentsForAccount(accountId, requestOptions);
+        assertEquals(invoicePaymentsForAccount.size(), 2);
+
+        // INVOICE PAYMENT FOR ORIGINAL PURCHASE
+        final InvoicePayment invoicePaymentPurchase = invoicePaymentsForAccount.get(0);
+        assertEquals(invoicePaymentPurchase.getPurchasedAmount().compareTo(BigDecimal.valueOf(249.95)), 0);
+        assertEquals(invoicePaymentPurchase.getCreditedAmount().compareTo(BigDecimal.ZERO), 0);
+        assertEquals(invoicePaymentPurchase.getTransactions().get(0).getTransactionType(), TransactionType.PURCHASE.toString());
+        assertEquals(invoicePaymentPurchase.getTransactions().get(0).getAmount().compareTo(BigDecimal.valueOf(249.95)), 0);
+
+        // INVOICE PAYMENT FOR EXTERNAL REFUND
+        final InvoicePayment creditInvoicePayment = invoicePaymentsForAccount.get(1);
+        assertTrue(creditInvoicePayment.equals(invoicePaymentExternalRefund));
+
+        assertEquals(creditInvoicePayment.getPurchasedAmount().compareTo(BigDecimal.ZERO), 0);
+        assertEquals(creditInvoicePayment.getCreditedAmount().compareTo(BigDecimal.valueOf(249.95)), 0);
+        assertEquals(creditInvoicePayment.getTransactions().size(), 1);
+        assertEquals(creditInvoicePayment.getTransactions().get(0).getTransactionType(), TransactionType.CREDIT.toString());
+        assertEquals(creditInvoicePayment.getTransactions().get(0).getAmount().compareTo(BigDecimal.valueOf(249.95)), 0);
+    }
+
+    private void assertSingleInvoicePaymentRefund(final InvoicePayment invoicePaymentRefund) {
+        // ONLY ONE INVOICE PAYMENT IS GENERATED FOR BOTH, PURCHASE AND REFUND.
+        assertEquals(invoicePaymentRefund.getPurchasedAmount().compareTo(BigDecimal.valueOf(249.95)), 0);
+        assertEquals(invoicePaymentRefund.getRefundedAmount().compareTo(BigDecimal.valueOf(249.95)), 0);
+
+        assertEquals(invoicePaymentRefund.getTransactions().size(), 2);
+        assertEquals(invoicePaymentRefund.getTransactions().get(0).getTransactionType(), TransactionType.PURCHASE.toString());
+        assertEquals(invoicePaymentRefund.getTransactions().get(0).getAmount().compareTo(BigDecimal.valueOf(249.95)), 0);
+        assertEquals(invoicePaymentRefund.getTransactions().get(1).getTransactionType(), TransactionType.REFUND.toString());
+        assertEquals(invoicePaymentRefund.getTransactions().get(1).getAmount().compareTo(BigDecimal.valueOf(249.95)), 0);
+    }
+
 }