killbill-uncached

Fix issues with chargeback

7/18/2012 4:41:56 PM

Details

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 2729faa..effca13 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
@@ -22,6 +22,7 @@ import java.util.List;
 import java.util.UUID;
 
 import org.joda.time.DateTime;
+import org.skife.jdbi.v2.exceptions.TransactionFailedException;
 
 import com.ning.billing.ErrorCode;
 import com.ning.billing.catalog.api.Currency;
@@ -38,6 +39,8 @@ import com.google.inject.Inject;
 
 public class DefaultInvoicePaymentApi implements InvoicePaymentApi {
 
+    private static final WithInvoiceApiException<InvoicePayment> invoicePaymentWithException = new WithInvoiceApiException<InvoicePayment>();
+
     private final InvoiceDao dao;
 
     @Inject
@@ -77,15 +80,6 @@ public class DefaultInvoicePaymentApi implements InvoicePaymentApi {
         dao.notifyOfPayment(invoicePayment, context);
     }
 
-    @Override
-    public InvoicePayment createChargeback(final UUID invoicePaymentId, final BigDecimal amount, final CallContext context) throws InvoiceApiException {
-        return dao.postChargeback(invoicePaymentId, amount, context);
-    }
-
-    @Override
-    public InvoicePayment createChargeback(final UUID invoicePaymentId, final CallContext context) throws InvoiceApiException {
-        return createChargeback(invoicePaymentId, null, context);
-    }
 
     @Override
     public BigDecimal getRemainingAmountPaid(final UUID invoicePaymentId) {
@@ -115,9 +109,59 @@ 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 {
-        if (amount.compareTo(BigDecimal.ZERO) <= 0) {
-            throw new InvoiceApiException(ErrorCode.PAYMENT_REFUND_AMOUNT_NEGATIVE_OR_NULL);
+
+        return invoicePaymentWithException.executeAndThrow(new WithInvoiceApiExceptionCallback<InvoicePayment>() {
+
+            @Override
+            public InvoicePayment doHandle() throws InvoiceApiException {
+                if (amount.compareTo(BigDecimal.ZERO) <= 0) {
+                    throw new InvoiceApiException(ErrorCode.PAYMENT_REFUND_AMOUNT_NEGATIVE_OR_NULL);
+                }
+                return dao.createRefund(paymentId, amount, isInvoiceAdjusted, paymentCookieId, context);
+            }
+        });
+    }
+
+    @Override
+    public InvoicePayment createChargeback(final UUID invoicePaymentId, final CallContext context) throws InvoiceApiException {
+        return createChargeback(invoicePaymentId, null, context);
+    }
+
+
+    @Override
+    public InvoicePayment createChargeback(final UUID invoicePaymentId, final BigDecimal amount, final CallContext context) throws InvoiceApiException {
+
+        return invoicePaymentWithException.executeAndThrow(new WithInvoiceApiExceptionCallback<InvoicePayment>() {
+
+            @Override
+            public InvoicePayment doHandle() throws InvoiceApiException {
+                return dao.postChargeback(invoicePaymentId, amount, context);
+            }
+
+        });
+
+    }
+
+    //
+    // Allow to safely catch TransactionFailedException exceptions and rethrow the correct InvoiceApiException exception
+    //
+    private interface WithInvoiceApiExceptionCallback<T> {
+        public T doHandle() throws InvoiceApiException;
+    }
+
+    private static final class WithInvoiceApiException<T> {
+        public T executeAndThrow(WithInvoiceApiExceptionCallback<T> callback) throws InvoiceApiException  {
+
+            try {
+                return callback.doHandle();
+            } catch (TransactionFailedException e) {
+                if (e.getCause() instanceof InvoiceApiException) {
+                    InvoiceApiException realException = (InvoiceApiException) e.getCause();
+                    throw realException;
+                } else {
+                    throw e;
+                }
+            }
         }
-        return dao.createRefund(paymentId, amount, isInvoiceAdjusted, 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 3f94759..68cf45c 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
@@ -385,7 +385,7 @@ public class DefaultInvoiceDao implements InvoiceDao {
                 if (payment == null) {
                     throw new InvoiceApiException(ErrorCode.INVOICE_PAYMENT_NOT_FOUND, invoicePaymentId.toString());
                 } else {
-                    final InvoicePayment chargeBack = new DefaultInvoicePayment(UUID.randomUUID(), InvoicePaymentType.CHARGED_BACK, null,
+                    final InvoicePayment chargeBack = new DefaultInvoicePayment(UUID.randomUUID(), InvoicePaymentType.CHARGED_BACK, payment.getPaymentId(),
                                                                                 payment.getInvoiceId(), context.getCreatedDate(), requestedChargedBackAmout.negate(), payment.getCurrency(), null, payment.getId());
                     invoicePaymentSqlDao.create(chargeBack, context);
                     return chargeBack;
diff --git a/jaxrs/src/main/java/com/ning/billing/jaxrs/json/ChargebackJson.java b/jaxrs/src/main/java/com/ning/billing/jaxrs/json/ChargebackJson.java
index 2cb35fc..9eba9a9 100644
--- a/jaxrs/src/main/java/com/ning/billing/jaxrs/json/ChargebackJson.java
+++ b/jaxrs/src/main/java/com/ning/billing/jaxrs/json/ChargebackJson.java
@@ -49,7 +49,7 @@ public class ChargebackJson {
         this.requestedDate = null;
         this.effectiveDate = chargeback.getPaymentDate();
         this.chargebackAmount = chargeback.getAmount().negate();
-        this.paymentId = chargeback.getLinkedInvoicePaymentId().toString();
+        this.paymentId = chargeback.getPaymentId().toString();
         this.reason = null;
     }
 
diff --git a/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/ChargebackResource.java b/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/ChargebackResource.java
index 5920cc2..62d03e4 100644
--- a/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/ChargebackResource.java
+++ b/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/ChargebackResource.java
@@ -139,9 +139,7 @@ public class ChargebackResource implements JaxrsResource {
                                                                                   context.createContext(createdBy, reason, comment));
             return uriBuilder.buildResponse(ChargebackResource.class, "getChargeback", chargeBack.getId());
         } catch (InvoiceApiException e) {
-            final String error = String.format("Failed to create chargeback %s", json);
-            log.info(error, e);
-            return Response.status(Response.Status.BAD_REQUEST).entity(error).build();
+            return Response.status(Response.Status.BAD_REQUEST).entity(e.getMessage()).build();
         } catch (IllegalArgumentException e) {
             return Response.status(Response.Status.BAD_REQUEST).entity(e.getMessage()).build();
         }
diff --git a/server/src/test/java/com/ning/billing/jaxrs/TestChargeback.java b/server/src/test/java/com/ning/billing/jaxrs/TestChargeback.java
index 34cf968..386c3b2 100644
--- a/server/src/test/java/com/ning/billing/jaxrs/TestChargeback.java
+++ b/server/src/test/java/com/ning/billing/jaxrs/TestChargeback.java
@@ -45,6 +45,7 @@ import static org.testng.Assert.assertNotNull;
 import static org.testng.Assert.assertTrue;
 
 public class TestChargeback extends TestJaxrsBase {
+
     @Test(groups = "slow")
     public void testAddChargeback() throws Exception {
         final PaymentJsonSimple payment = createAccountWithInvoiceAndPayment();
@@ -61,6 +62,7 @@ public class TestChargeback extends TestJaxrsBase {
         response = doGetWithUrl(location, DEFAULT_EMPTY_QUERY, DEFAULT_HTTP_TIMEOUT_SEC);
         verifySingleChargebackResponse(response, input);
 
+
         // Find the chargeback by account
         response = doGet(JaxrsResource.CHARGEBACKS_PATH + "/accounts/" + payment.getAccountId(), DEFAULT_EMPTY_QUERY, DEFAULT_HTTP_TIMEOUT_SEC);
         verifyCollectionChargebackResponse(response, input);
@@ -70,11 +72,55 @@ public class TestChargeback extends TestJaxrsBase {
         verifyCollectionChargebackResponse(response, input);
     }
 
+
+    @Test(groups = "slow")
+    public void testMultipleChargeback() throws Exception {
+        final PaymentJsonSimple payment = createAccountWithInvoiceAndPayment();
+
+        // We get a 249.95 payment so we do 4 chargeback and then the fifth should fail
+        final ChargebackJson input = new ChargebackJson(null, null, new BigDecimal("50.00"), payment.getPaymentId(), null);
+        final String jsonInput = mapper.writeValueAsString(input);
+
+        //
+        int count = 4;
+        Response response = null;
+        while (count-- > 0) {
+            response = doPost(JaxrsResource.CHARGEBACKS_PATH, jsonInput, DEFAULT_EMPTY_QUERY, DEFAULT_HTTP_TIMEOUT_SEC);
+            assertEquals(response.getStatusCode(), javax.ws.rs.core.Response.Status.CREATED.getStatusCode(), response.getResponseBody());
+        }
+
+        // Last attempt should fail because this is more than the Payment
+        response = doPost(JaxrsResource.CHARGEBACKS_PATH, jsonInput, DEFAULT_EMPTY_QUERY, DEFAULT_HTTP_TIMEOUT_SEC);
+        assertEquals(response.getStatusCode(), javax.ws.rs.core.Response.Status.BAD_REQUEST.getStatusCode(), response.getResponseBody());
+
+
+        // Find the chargeback by account
+        response = doGet(JaxrsResource.CHARGEBACKS_PATH + "/accounts/" + payment.getAccountId(), DEFAULT_EMPTY_QUERY, DEFAULT_HTTP_TIMEOUT_SEC);
+        assertEquals(response.getStatusCode(), javax.ws.rs.core.Response.Status.OK.getStatusCode());
+        ChargebackCollectionJson objFromJson = mapper.readValue(response.getResponseBody(), ChargebackCollectionJson.class);
+        assertEquals(objFromJson.getChargebacks().size(), 4);
+        for (int i = 0; i < objFromJson.getChargebacks().size(); i++) {
+            ChargebackJson chargeBack = objFromJson.getChargebacks().get(i);
+            assertTrue(chargeBack.getChargebackAmount().compareTo(input.getChargebackAmount()) == 0);
+            assertEquals(chargeBack.getPaymentId(), input.getPaymentId());
+        }
+
+        // Find the chargeback by payment
+        response = doGet(JaxrsResource.CHARGEBACKS_PATH + "/payments/" + payment.getPaymentId(), DEFAULT_EMPTY_QUERY, DEFAULT_HTTP_TIMEOUT_SEC);
+        assertEquals(response.getStatusCode(), javax.ws.rs.core.Response.Status.OK.getStatusCode());
+        objFromJson = mapper.readValue(response.getResponseBody(), ChargebackCollectionJson.class);
+        assertEquals(objFromJson.getChargebacks().size(), 4);
+
+    }
+
+
     private void verifyCollectionChargebackResponse(final Response response, final ChargebackJson input) throws IOException {
         assertEquals(response.getStatusCode(), javax.ws.rs.core.Response.Status.OK.getStatusCode());
         final ChargebackCollectionJson objFromJson = mapper.readValue(response.getResponseBody(), ChargebackCollectionJson.class);
         assertEquals(objFromJson.getChargebacks().size(), 1);
-        assertTrue(objFromJson.getChargebacks().get(0).getChargebackAmount().compareTo(input.getChargebackAmount()) == 0);
+        ChargebackJson chargeBack = objFromJson.getChargebacks().get(0);
+        assertTrue(chargeBack.getChargebackAmount().compareTo(input.getChargebackAmount()) == 0);
+        assertEquals(chargeBack.getPaymentId(), input.getPaymentId());
     }
 
     private void verifySingleChargebackResponse(final Response response, final ChargebackJson input) throws IOException {