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 646a198..9c6eb69 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
@@ -44,9 +44,9 @@ public interface InvoicePaymentApi {
public void notifyOfPaymentAttempt(UUID invoiceId, UUID paymentAttemptId, DateTime paymentAttemptDate, CallContext context);
- public void processChargeback(UUID invoicePaymentId, BigDecimal amount, CallContext context) throws InvoiceApiException;
+ public InvoicePayment processChargeback(UUID invoicePaymentId, BigDecimal amount, CallContext context) throws InvoiceApiException;
- public void processChargeback(UUID invoicePaymentId, CallContext context) throws InvoiceApiException;
+ public InvoicePayment processChargeback(UUID invoicePaymentId, CallContext context) throws InvoiceApiException;
public BigDecimal getRemainingAmountPaid(UUID invoicePaymentId);
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 fca4789..a93ee28 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
@@ -80,16 +80,16 @@ public class DefaultInvoicePaymentApi implements InvoicePaymentApi {
}
@Override
- public void processChargeback(final UUID invoicePaymentId, final BigDecimal amount, final CallContext context) throws InvoiceApiException {
- dao.postChargeback(invoicePaymentId, amount, context);
+ public InvoicePayment processChargeback(final UUID invoicePaymentId, final BigDecimal amount, final CallContext context) throws InvoiceApiException {
+ return dao.postChargeback(invoicePaymentId, amount, context);
}
@Override
- public void processChargeback(final UUID invoicePaymentId, final CallContext context) throws InvoiceApiException {
+ public InvoicePayment processChargeback(final UUID invoicePaymentId, final CallContext context) throws InvoiceApiException {
// use the invoicePaymentId to get the amount remaining on the payment
// (preventing charge backs totalling more than the payment)
final BigDecimal amount = dao.getRemainingAmountPaid(invoicePaymentId);
- processChargeback(invoicePaymentId, amount, context);
+ return processChargeback(invoicePaymentId, amount, context);
}
@Override
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 22ddb71..7102051 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
@@ -255,7 +255,7 @@ public class DefaultInvoiceDao implements InvoiceDao {
@Override
public InvoicePayment getInvoicePayment(final UUID paymentAttemptId) {
- return invoicePaymentSqlDao.getInvoicePayment(paymentAttemptId);
+ return invoicePaymentSqlDao.getInvoicePayment(paymentAttemptId.toString());
}
@Override
@@ -269,18 +269,25 @@ public class DefaultInvoiceDao implements InvoiceDao {
}
@Override
- public void postChargeback(final UUID invoicePaymentId, final BigDecimal amount, final CallContext context) throws InvoiceApiException {
- final InvoicePayment payment = invoicePaymentSqlDao.getById(invoicePaymentId.toString());
- if (payment == null) {
- throw new InvoiceApiException(ErrorCode.INVOICE_PAYMENT_NOT_FOUND, invoicePaymentId.toString());
- } else {
- if (amount.compareTo(BigDecimal.ZERO) < 0) {
- throw new InvoiceApiException(ErrorCode.CHARGE_BACK_AMOUNT_IS_NEGATIVE);
+ public InvoicePayment postChargeback(final UUID invoicePaymentId, final BigDecimal amount, final CallContext context) throws InvoiceApiException {
+ return invoicePaymentSqlDao.inTransaction(new Transaction<InvoicePayment, InvoicePaymentSqlDao>() {
+ @Override
+ public InvoicePayment inTransaction(final InvoicePaymentSqlDao transactional, final TransactionStatus status) throws Exception {
+ final InvoicePayment payment = invoicePaymentSqlDao.getById(invoicePaymentId.toString());
+ if (payment == null) {
+ throw new InvoiceApiException(ErrorCode.INVOICE_PAYMENT_NOT_FOUND, invoicePaymentId.toString());
+ } else {
+ if (amount.compareTo(BigDecimal.ZERO) < 0) {
+ throw new InvoiceApiException(ErrorCode.CHARGE_BACK_AMOUNT_IS_NEGATIVE);
+ }
+
+ final InvoicePayment chargeBack = payment.asChargeBack(amount, context.getCreatedDate());
+ invoicePaymentSqlDao.create(chargeBack, context);
+
+ return chargeBack;
+ }
}
-
- final InvoicePayment chargeBack = payment.asChargeBack(amount, context.getCreatedDate());
- invoicePaymentSqlDao.create(chargeBack, context);
- }
+ });
}
@Override
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 fe82d6c..888295a 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
@@ -61,7 +61,7 @@ public interface InvoiceDao {
void removeWrittenOff(final UUID invoiceId, final CallContext context) throws TagApiException;
- void postChargeback(final UUID invoicePaymentId, final BigDecimal amount, final CallContext context) throws InvoiceApiException;
+ InvoicePayment postChargeback(final UUID invoicePaymentId, final BigDecimal amount, final CallContext context) throws InvoiceApiException;
BigDecimal getRemainingAmountPaid(final UUID invoicePaymentId);
diff --git a/invoice/src/main/java/com/ning/billing/invoice/dao/InvoicePaymentSqlDao.java b/invoice/src/main/java/com/ning/billing/invoice/dao/InvoicePaymentSqlDao.java
index ebbdbb7..8b12f8d 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/dao/InvoicePaymentSqlDao.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/dao/InvoicePaymentSqlDao.java
@@ -78,7 +78,7 @@ public interface InvoicePaymentSqlDao extends EntitySqlDao<InvoicePayment>, Tran
public List<InvoicePayment> getPaymentsForInvoice(@Bind("invoiceId") final String invoiceId);
@SqlQuery
- InvoicePayment getInvoicePayment(@Bind("paymentAttemptId") final UUID paymentAttemptId);
+ InvoicePayment getInvoicePayment(@Bind("paymentAttemptId") final String paymentAttemptId);
@SqlUpdate
void notifyOfPaymentAttempt(@InvoicePaymentBinder final InvoicePayment invoicePayment,
diff --git a/invoice/src/main/resources/com/ning/billing/invoice/dao/InvoicePaymentSqlDao.sql.stg b/invoice/src/main/resources/com/ning/billing/invoice/dao/InvoicePaymentSqlDao.sql.stg
index 3773a1c..8ccf73b 100644
--- a/invoice/src/main/resources/com/ning/billing/invoice/dao/InvoicePaymentSqlDao.sql.stg
+++ b/invoice/src/main/resources/com/ning/billing/invoice/dao/InvoicePaymentSqlDao.sql.stg
@@ -27,7 +27,7 @@ batchCreateFromTransaction() ::= <<
getByPaymentAttemptId() ::= <<
SELECT <invoicePaymentFields()>
FROM invoice_payments
- WHERE payment_id = :paymentAttemptId;
+ WHERE payment_attempt_id = :paymentAttemptId;
>>
get() ::= <<
@@ -56,7 +56,7 @@ notifyOfPaymentAttempt() ::= <<
getInvoicePayment() ::= <<
SELECT <invoicePaymentFields()>
FROM invoice_payments
- WHERE payment_id = :payment_id;
+ WHERE payment_attempt_id = :paymentAttemptId;
>>
getRecordId() ::= <<
@@ -119,4 +119,4 @@ getChargebacksByAttemptPaymentId() ::= <<
WHERE reversed_invoice_payment_id IN
(SELECT id FROM invoice_payments WHERE payment_attempt_id = :paymentAttemptId);
>>
-;
\ No newline at end of file
+;
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 e6349c9..d4feb57 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
@@ -102,7 +102,7 @@ public class MockInvoicePaymentApi implements InvoicePaymentApi {
}
@Override
- public void processChargeback(final UUID invoicePaymentId, final BigDecimal amount, final CallContext context) throws InvoiceApiException {
+ public InvoicePayment processChargeback(final UUID invoicePaymentId, final BigDecimal amount, final CallContext context) throws InvoiceApiException {
InvoicePayment existingPayment = null;
for (final InvoicePayment payment : invoicePayments) {
if (payment.getId() == invoicePaymentId) {
@@ -113,10 +113,12 @@ public class MockInvoicePaymentApi implements InvoicePaymentApi {
if (existingPayment != null) {
invoicePayments.add(existingPayment.asChargeBack(amount, DateTime.now(DateTimeZone.UTC)));
}
+
+ return existingPayment;
}
@Override
- public void processChargeback(final UUID invoicePaymentId, final CallContext context) throws InvoiceApiException {
+ public InvoicePayment processChargeback(final UUID invoicePaymentId, final CallContext context) throws InvoiceApiException {
InvoicePayment existingPayment = null;
for (final InvoicePayment payment : invoicePayments) {
if (payment.getId() == invoicePaymentId) {
@@ -127,6 +129,8 @@ public class MockInvoicePaymentApi implements InvoicePaymentApi {
if (existingPayment != null) {
this.processChargeback(invoicePaymentId, existingPayment.getAmount(), context);
}
+
+ return existingPayment;
}
@Override
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 68987f8..0564bd0 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
@@ -213,7 +213,7 @@ public class MockInvoiceDao implements InvoiceDao {
}
@Override
- public void postChargeback(final UUID invoicePaymentId, final BigDecimal amount, final CallContext context) throws InvoiceApiException {
+ public InvoicePayment postChargeback(final UUID invoicePaymentId, final BigDecimal amount, final CallContext context) throws InvoiceApiException {
throw new UnsupportedOperationException();
}
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 9420e7c..8823018 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
@@ -114,20 +114,22 @@ public class ChargebackResource implements JaxrsResource {
}
});
if (attempts.size() == 0) {
- final String error = String.format("Failed to locate succesful payment attempts for paymentId %s", paymentId);
+ final String error = String.format("Failed to locate successful payment attempts for paymentId %s", paymentId);
return Response.status(Response.Status.NO_CONTENT).entity(error).build();
}
final UUID paymentAttemptId = attempts.iterator().next().getId();
final List<InvoicePayment> chargebacks = invoicePaymentApi.getChargebacksByPaymentAttemptId(paymentAttemptId);
- final List<ChargebackJson> chargebacksJson = convertToJson(chargebacks);
-
- final String accountId = invoicePaymentApi.getAccountIdFromInvoicePaymentId(UUID.fromString(paymentId)).toString();
-
+ if (chargebacks.size() == 0) {
+ return Response.status(Response.Status.NO_CONTENT).build();
+ }
+ final UUID invoicePaymentId = chargebacks.get(0).getId();
+ final String accountId = invoicePaymentApi.getAccountIdFromInvoicePaymentId(invoicePaymentId).toString();
+ final List<ChargebackJson> chargebacksJson = convertToJson(chargebacks);
final ChargebackCollectionJson json = new ChargebackCollectionJson(accountId, chargebacksJson);
- return Response.status(Response.Status.OK).entity(json).build();
+ return Response.status(Response.Status.OK).entity(json).build();
} catch (PaymentApiException e) {
final String error = String.format("Failed to locate payment attempt for payment id %s", paymentId);
return Response.status(Response.Status.NO_CONTENT).entity(error).build();
@@ -145,15 +147,36 @@ public class ChargebackResource implements JaxrsResource {
@HeaderParam(HDR_REASON) final String reason,
@HeaderParam(HDR_COMMENT) final String comment) {
try {
- invoicePaymentApi.processChargeback(UUID.fromString(json.getPaymentId()), json.getChargebackAmount(),
- context.createContext(createdBy, reason, comment));
- return uriBuilder.buildResponse(ChargebackResource.class, "getChargeback", json.getPaymentId());
+ final Payment payment = paymentApi.getPayment(UUID.fromString(json.getPaymentId()));
+ final Collection<PaymentAttempt> attempts = Collections2.filter(payment.getAttempts(), new Predicate<PaymentAttempt>() {
+ @Override
+ public boolean apply(final PaymentAttempt input) {
+ return input.getPaymentStatus() == PaymentStatus.SUCCESS;
+ }
+ });
+ if (attempts.size() == 0) {
+ final String error = String.format("Failed to locate successful payment attempts for paymentId %s", json.getPaymentId());
+ return Response.status(Response.Status.NO_CONTENT).entity(error).build();
+ }
+
+ final UUID paymentAttemptId = attempts.iterator().next().getId();
+ final InvoicePayment invoicePayment = invoicePaymentApi.getInvoicePayment(paymentAttemptId);
+ if (invoicePayment == null) {
+ final String error = String.format("Failed to locate invoice payment for paymentAttemptId %s", paymentAttemptId);
+ return Response.status(Response.Status.NO_CONTENT).entity(error).build();
+ }
+
+ final InvoicePayment chargeBack = invoicePaymentApi.processChargeback(invoicePayment.getId(), json.getChargebackAmount(),
+ 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();
} catch (IllegalArgumentException e) {
return Response.status(Response.Status.BAD_REQUEST).entity(e.getMessage()).build();
+ } catch (PaymentApiException 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 8627a0d..9dc8d51 100644
--- a/server/src/test/java/com/ning/billing/jaxrs/TestChargeback.java
+++ b/server/src/test/java/com/ning/billing/jaxrs/TestChargeback.java
@@ -18,40 +18,42 @@ package com.ning.billing.jaxrs;
import java.io.IOException;
import java.math.BigDecimal;
+import java.util.List;
import java.util.UUID;
import org.joda.time.DateTime;
import org.joda.time.DateTimeZone;
import org.testng.Assert;
-import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
-import com.ning.billing.invoice.api.InvoicePayment;
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.google.common.collect.ImmutableMap;
+import com.ning.billing.catalog.api.BillingPeriod;
+import com.ning.billing.catalog.api.ProductCategory;
+import com.ning.billing.jaxrs.json.AccountJson;
+import com.ning.billing.jaxrs.json.BundleJsonNoSubscriptions;
import com.ning.billing.jaxrs.json.ChargebackCollectionJson;
import com.ning.billing.jaxrs.json.ChargebackJson;
+import com.ning.billing.jaxrs.json.InvoiceJsonSimple;
+import com.ning.billing.jaxrs.json.PaymentJsonSimple;
+import com.ning.billing.jaxrs.json.SubscriptionJsonNoEvents;
import com.ning.billing.jaxrs.resources.JaxrsResource;
import com.ning.http.client.Response;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertNotNull;
+import static org.testng.Assert.assertTrue;
public class TestChargeback extends TestJaxrsBase {
- private final String accountId = UUID.randomUUID().toString();
-
- @BeforeMethod(groups = "slow")
- public void setUp() throws Exception {
- final InvoicePayment invoicePayment = createInvoicePayment();
- }
-
- @Test(groups = "slow", enabled = false)
+ @Test(groups = "slow")
public void testAddChargeback() throws Exception {
- final ChargebackJson input = new ChargebackJson(new DateTime(DateTimeZone.UTC), new DateTime(DateTimeZone.UTC),
- BigDecimal.TEN, UUID.randomUUID().toString(), UUID.randomUUID().toString());
+ final PaymentJsonSimple payment = createInvoicePayment();
+ final ChargebackJson input = new ChargebackJson(null, null, BigDecimal.TEN, payment.getPaymentId(), null);
final String jsonInput = mapper.writeValueAsString(input);
// Create the chargeback
Response response = doPost(JaxrsResource.CHARGEBACKS_PATH, jsonInput, DEFAULT_EMPTY_QUERY, DEFAULT_HTTP_TIMEOUT_SEC);
- assertEquals(response.getStatusCode(), javax.ws.rs.core.Response.Status.OK.getStatusCode(), response.getResponseBody());
+ assertEquals(response.getStatusCode(), javax.ws.rs.core.Response.Status.CREATED.getStatusCode(), response.getResponseBody());
// Find the chargeback by location
final String location = response.getHeader("Location");
@@ -60,11 +62,11 @@ public class TestChargeback extends TestJaxrsBase {
verifySingleChargebackResponse(response, input);
// Find the chargeback by account
- response = doGet(JaxrsResource.CHARGEBACKS_PATH + "/accounts/" + accountId, DEFAULT_EMPTY_QUERY, DEFAULT_HTTP_TIMEOUT_SEC);
+ response = doGet(JaxrsResource.CHARGEBACKS_PATH + "/accounts/" + payment.getAccountId(), DEFAULT_EMPTY_QUERY, DEFAULT_HTTP_TIMEOUT_SEC);
verifyCollectionChargebackResponse(response, input);
// Find the chargeback by payment
- response = doGet(JaxrsResource.CHARGEBACKS_PATH + "/payments/" + input.getPaymentId(), DEFAULT_EMPTY_QUERY, DEFAULT_HTTP_TIMEOUT_SEC);
+ response = doGet(JaxrsResource.CHARGEBACKS_PATH + "/payments/" + payment.getPaymentId(), DEFAULT_EMPTY_QUERY, DEFAULT_HTTP_TIMEOUT_SEC);
verifyCollectionChargebackResponse(response, input);
}
@@ -72,13 +74,13 @@ public class TestChargeback extends TestJaxrsBase {
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);
- assertEquals(objFromJson.getChargebacks().get(0), input);
+ assertTrue(objFromJson.getChargebacks().get(0).getChargebackAmount().compareTo(input.getChargebackAmount()) == 0);
}
private void verifySingleChargebackResponse(final Response response, final ChargebackJson input) throws IOException {
assertEquals(response.getStatusCode(), javax.ws.rs.core.Response.Status.OK.getStatusCode());
final ChargebackJson objFromJson = mapper.readValue(response.getResponseBody(), ChargebackJson.class);
- assertEquals(objFromJson, input);
+ assertTrue(objFromJson.getChargebackAmount().compareTo(input.getChargebackAmount()) == 0);
}
@Test(groups = "slow")
@@ -122,8 +124,50 @@ public class TestChargeback extends TestJaxrsBase {
//assertEquals(response.getStatusCode(), javax.ws.rs.core.Response.Status.NO_CONTENT.getStatusCode(), response.getResponseBody());
}
- private InvoicePayment createInvoicePayment() {
- // TODO - blocked on payment resource
- return null;
+ private PaymentJsonSimple createInvoicePayment() throws Exception {
+ final InvoiceJsonSimple invoice = createInvoice();
+ return getPayment(invoice);
+ }
+
+ private InvoiceJsonSimple createInvoice() throws Exception {
+ // Create account
+ final AccountJson accountJson = createAccountWithDefaultPaymentMethod(UUID.randomUUID().toString(), UUID.randomUUID().toString(), "nohup@yahoo.com");
+
+ // Create bundle
+ final BundleJsonNoSubscriptions bundleJson = createBundle(accountJson.getAccountId(), UUID.randomUUID().toString());
+ assertNotNull(bundleJson);
+
+ // Create subscription
+ final SubscriptionJsonNoEvents subscriptionJson = createSubscription(bundleJson.getBundleId(), "Shotgun", ProductCategory.BASE.toString(), BillingPeriod.MONTHLY.toString(), true);
+ assertNotNull(subscriptionJson);
+
+ // Move after the trial period to trigger an invoice with a non-zero invoice item
+ clock.addDays(32);
+ crappyWaitForLackOfProperSynchonization();
+
+ // Retrieve the invoice
+ final Response response = doGet(JaxrsResource.INVOICES_PATH, ImmutableMap.<String, String>of(JaxrsResource.QUERY_ACCOUNT_ID, accountJson.getAccountId()), DEFAULT_HTTP_TIMEOUT_SEC);
+ Assert.assertEquals(response.getStatusCode(), javax.ws.rs.core.Response.Status.OK.getStatusCode());
+ final String baseJson = response.getResponseBody();
+ final List<InvoiceJsonSimple> objFromJson = mapper.readValue(baseJson, new TypeReference<List<InvoiceJsonSimple>>() {});
+ assertNotNull(objFromJson);
+ // We should have two invoices, one for the trial (zero dollar amount) and one for the first month
+ assertEquals(objFromJson.size(), 2);
+ assertTrue(objFromJson.get(1).getAmount().doubleValue() > 0);
+
+ return objFromJson.get(1);
+ }
+
+ private PaymentJsonSimple getPayment(final InvoiceJsonSimple invoice) throws IOException {
+ final String uri = JaxrsResource.INVOICES_PATH + "/" + invoice.getInvoiceId() + "/" + JaxrsResource.PAYMENTS;
+ final Response response = doGet(uri, DEFAULT_EMPTY_QUERY, DEFAULT_HTTP_TIMEOUT_SEC);
+
+ Assert.assertEquals(response.getStatusCode(), javax.ws.rs.core.Response.Status.OK.getStatusCode());
+ final String baseJson = response.getResponseBody();
+ final List<PaymentJsonSimple> objFromJson = mapper.readValue(baseJson, new TypeReference<List<PaymentJsonSimple>>() {});
+ assertNotNull(objFromJson);
+ assertEquals(objFromJson.size(), 1);
+
+ return objFromJson.get(0);
}
}