killbill-memoizeit
Changes
invoice/src/test/java/org/killbill/billing/invoice/generator/TestDefaultInvoiceGenerator.java 2(+1 -1)
invoice/src/test/java/org/killbill/billing/invoice/template/formatters/TestDefaultInvoiceFormatter.java 4(+2 -2)
payment/src/main/java/org/killbill/billing/payment/core/sm/control/OperationControlCallback.java 52(+26 -26)
payment/src/main/java/org/killbill/billing/payment/invoice/InvoicePaymentControlPluginApi.java 22(+20 -2)
pom.xml 2(+1 -1)
Details
diff --git a/api/src/main/java/org/killbill/billing/invoice/api/InvoiceInternalApi.java b/api/src/main/java/org/killbill/billing/invoice/api/InvoiceInternalApi.java
index c54ef47..0403e60 100644
--- a/api/src/main/java/org/killbill/billing/invoice/api/InvoiceInternalApi.java
+++ b/api/src/main/java/org/killbill/billing/invoice/api/InvoiceInternalApi.java
@@ -37,7 +37,7 @@ public interface InvoiceInternalApi {
public BigDecimal getAccountBalance(UUID accountId, InternalTenantContext context);
- public void notifyOfPayment(UUID invoiceId, BigDecimal amountOutstanding, Currency currency, Currency processedCurrency, UUID paymentId, DateTime paymentDate, InternalCallContext context) throws InvoiceApiException;
+ public void notifyOfPayment(UUID invoiceId, BigDecimal amountOutstanding, Currency currency, Currency processedCurrency, UUID paymentId, DateTime paymentDate, boolean success, InternalCallContext context) throws InvoiceApiException;
public void notifyOfPayment(InvoicePayment invoicePayment, InternalCallContext context) throws InvoiceApiException;
diff --git a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestInvoicePayment.java b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestInvoicePayment.java
index e68aab1..13a3a43 100644
--- a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestInvoicePayment.java
+++ b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestInvoicePayment.java
@@ -18,21 +18,29 @@
package org.killbill.billing.beatrix.integration;
import java.math.BigDecimal;
+import java.util.List;
import org.joda.time.LocalDate;
import org.killbill.billing.ObjectType;
import org.killbill.billing.account.api.Account;
import org.killbill.billing.account.api.AccountData;
import org.killbill.billing.api.TestApiListener.NextEvent;
+import org.killbill.billing.catalog.api.BillingPeriod;
import org.killbill.billing.catalog.api.Currency;
+import org.killbill.billing.catalog.api.ProductCategory;
+import org.killbill.billing.entitlement.api.DefaultEntitlement;
import org.killbill.billing.invoice.api.Invoice;
import org.killbill.billing.invoice.api.InvoiceItem;
+import org.killbill.billing.invoice.api.InvoiceItemType;
import org.killbill.billing.invoice.model.ExternalChargeInvoiceItem;
import org.killbill.billing.payment.api.Payment;
+import org.killbill.billing.payment.api.PluginProperty;
import org.testng.annotations.Test;
import com.google.common.collect.ImmutableList;
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertFalse;
import static org.testng.Assert.assertTrue;
public class TestInvoicePayment extends TestIntegrationBase {
@@ -97,4 +105,58 @@ public class TestInvoicePayment extends TestIntegrationBase {
assertTrue(accountBalance.compareTo(new BigDecimal("4.00")) == 0);
}
+
+ //
+
+ @Test(groups = "slow")
+ public void testWithPaymentFailure() throws Exception {
+
+ clock.setDay(new LocalDate(2012, 4, 1));
+
+ final AccountData accountData = getAccountData(1);
+ final Account account = createAccountWithNonOsgiPaymentMethod(accountData);
+ accountChecker.checkAccount(account.getId(), accountData, callContext);
+
+ paymentPlugin.makeNextPaymentFailWithError();
+
+ createBaseEntitlementAndCheckForCompletion(account.getId(), "bundleKey", "Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, NextEvent.CREATE, NextEvent.INVOICE);
+
+ busHandler.pushExpectedEvents(NextEvent.PHASE, NextEvent.INVOICE, NextEvent.PAYMENT_ERROR);
+ clock.addDays(30);
+ assertListenerStatus();
+
+ final List<Invoice> invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), callContext);
+ assertEquals(invoices.size(), 2);
+
+ final Invoice invoice1 = invoices.get(0).getInvoiceItems().get(0).getInvoiceItemType() == InvoiceItemType.RECURRING ?
+ invoices.get(0) : invoices.get(1);
+ assertTrue(invoice1.getBalance().compareTo(new BigDecimal("249.95")) == 0);
+ assertTrue(invoice1.getPaidAmount().compareTo(BigDecimal.ZERO) == 0);
+ assertTrue(invoice1.getChargedAmount().compareTo(new BigDecimal("249.95")) == 0);
+ assertEquals(invoice1.getPayments().size(), 1);
+ assertFalse(invoice1.getPayments().get(0).isSuccess());
+
+ BigDecimal accountBalance1 = invoiceUserApi.getAccountBalance(account.getId(), callContext);
+ assertTrue(accountBalance1.compareTo(new BigDecimal("249.95")) == 0);
+
+ final List<Payment> payments = paymentApi.getAccountPayments(account.getId(), false, ImmutableList.<PluginProperty>of(), callContext);
+ assertEquals(payments.size(), 1);
+
+ // Trigger the payment retry
+ busHandler.pushExpectedEvents(NextEvent.PAYMENT);
+ clock.addDays(8);
+ assertListenerStatus();
+
+ Invoice invoice2 = invoiceUserApi.getInvoice(invoice1.getId(), callContext);
+ assertTrue(invoice2.getBalance().compareTo(BigDecimal.ZERO) == 0);
+ assertTrue(invoice2.getPaidAmount().compareTo(new BigDecimal("249.95")) == 0);
+ assertTrue(invoice2.getChargedAmount().compareTo(new BigDecimal("249.95")) == 0);
+ assertEquals(invoice2.getPayments().size(), 1);
+ assertTrue(invoice2.getPayments().get(0).isSuccess());
+
+ BigDecimal accountBalance2 = invoiceUserApi.getAccountBalance(account.getId(), callContext);
+ assertTrue(accountBalance2.compareTo(BigDecimal.ZERO) == 0);
+ }
+
+
}
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/api/svcs/DefaultInvoiceInternalApi.java b/invoice/src/main/java/org/killbill/billing/invoice/api/svcs/DefaultInvoiceInternalApi.java
index db51bbb..07f788b 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/api/svcs/DefaultInvoiceInternalApi.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/api/svcs/DefaultInvoiceInternalApi.java
@@ -94,8 +94,8 @@ public class DefaultInvoiceInternalApi implements InvoiceInternalApi {
}
@Override
- public void notifyOfPayment(final UUID invoiceId, final BigDecimal amount, final Currency currency, final Currency processedCurrency, final UUID paymentId, final DateTime paymentDate, final InternalCallContext context) throws InvoiceApiException {
- final InvoicePayment invoicePayment = new DefaultInvoicePayment(InvoicePaymentType.ATTEMPT, paymentId, invoiceId, paymentDate, amount, currency, processedCurrency);
+ public void notifyOfPayment(final UUID invoiceId, final BigDecimal amount, final Currency currency, final Currency processedCurrency, final UUID paymentId, final DateTime paymentDate, final boolean success, final InternalCallContext context) throws InvoiceApiException {
+ final InvoicePayment invoicePayment = new DefaultInvoicePayment(InvoicePaymentType.ATTEMPT, paymentId, invoiceId, paymentDate, amount, currency, processedCurrency, success);
notifyOfPayment(invoicePayment, context);
}
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/calculator/InvoiceCalculatorUtils.java b/invoice/src/main/java/org/killbill/billing/invoice/calculator/InvoiceCalculatorUtils.java
index 1437745..eceab78 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/calculator/InvoiceCalculatorUtils.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/calculator/InvoiceCalculatorUtils.java
@@ -170,6 +170,9 @@ public abstract class InvoiceCalculatorUtils {
}
for (final InvoicePayment invoicePayment : invoicePayments) {
+ if (!invoicePayment.isSuccess()) {
+ continue;
+ }
if (InvoicePaymentType.ATTEMPT.equals(invoicePayment.getType())) {
amountPaid = amountPaid.add(invoicePayment.getAmount());
}
@@ -185,6 +188,9 @@ public abstract class InvoiceCalculatorUtils {
}
for (final InvoicePayment invoicePayment : invoicePayments) {
+ if (!invoicePayment.isSuccess()) {
+ continue;
+ }
if (InvoicePaymentType.REFUND.equals(invoicePayment.getType()) ||
InvoicePaymentType.CHARGED_BACK.equals(invoicePayment.getType())) {
amountRefunded = amountRefunded.add(invoicePayment.getAmount());
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/dao/DefaultInvoiceDao.java b/invoice/src/main/java/org/killbill/billing/invoice/dao/DefaultInvoiceDao.java
index 655c5da..0f4cdc0 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/dao/DefaultInvoiceDao.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/dao/DefaultInvoiceDao.java
@@ -462,7 +462,7 @@ public class DefaultInvoiceDao extends EntityDaoBase<InvoiceModelDao, Invoice, I
final InvoicePaymentModelDao refund = new InvoicePaymentModelDao(UUIDs.randomUUID(), context.getCreatedDate(), InvoicePaymentType.REFUND,
payment.getInvoiceId(), paymentId,
context.getCreatedDate(), requestedPositiveAmount.negate(),
- payment.getCurrency(), payment.getProcessedCurrency(), transactionExternalKey, payment.getId());
+ payment.getCurrency(), payment.getProcessedCurrency(), transactionExternalKey, payment.getId(), true);
transactional.create(refund, context);
// Retrieve invoice after the Refund
@@ -548,7 +548,7 @@ public class DefaultInvoiceDao extends EntityDaoBase<InvoiceModelDao, Invoice, I
final InvoicePaymentModelDao chargeBack = new InvoicePaymentModelDao(UUIDs.randomUUID(), context.getCreatedDate(), InvoicePaymentType.CHARGED_BACK,
payment.getInvoiceId(), payment.getPaymentId(), context.getCreatedDate(),
requestedChargedBackAmount.negate(), payment.getCurrency(), payment.getProcessedCurrency(),
- null, payment.getId());
+ null, payment.getId(), true);
transactional.create(chargeBack, context);
// Notify the bus since the balance of the invoice changed
@@ -664,6 +664,8 @@ public class DefaultInvoiceDao extends EntityDaoBase<InvoiceModelDao, Invoice, I
}).orNull();
if (existingAttempt == null) {
transactional.create(invoicePayment, context);
+ } else if (!existingAttempt.getSuccess() && invoicePayment.getSuccess()) {
+ transactional.updateAttempt(existingAttempt.getRecordId(), invoicePayment.getPaymentDate().toDate(), invoicePayment.getAmount(), invoicePayment.getCurrency(), invoicePayment.getProcessedCurrency(), context);
}
return null;
}
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/dao/InvoicePaymentModelDao.java b/invoice/src/main/java/org/killbill/billing/invoice/dao/InvoicePaymentModelDao.java
index 20a7c81..e05448f 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/dao/InvoicePaymentModelDao.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/dao/InvoicePaymentModelDao.java
@@ -40,12 +40,13 @@ public class InvoicePaymentModelDao extends EntityModelDaoBase implements Entity
private Currency processedCurrency;
private String paymentCookieId;
private UUID linkedInvoicePaymentId;
+ private Boolean success;
public InvoicePaymentModelDao() { /* For the DAO mapper */ }
public InvoicePaymentModelDao(final UUID id, final DateTime createdDate, final InvoicePaymentType type, final UUID invoiceId,
final UUID paymentId, final DateTime paymentDate, final BigDecimal amount, final Currency currency,
- final Currency processedCurrency, final String paymentCookieId, final UUID linkedInvoicePaymentId) {
+ final Currency processedCurrency, final String paymentCookieId, final UUID linkedInvoicePaymentId, final Boolean success) {
super(id, createdDate, createdDate);
this.type = type;
this.invoiceId = invoiceId;
@@ -56,12 +57,13 @@ public class InvoicePaymentModelDao extends EntityModelDaoBase implements Entity
this.processedCurrency = processedCurrency;
this.paymentCookieId = paymentCookieId;
this.linkedInvoicePaymentId = linkedInvoicePaymentId;
+ this.success = success;
}
public InvoicePaymentModelDao(final InvoicePayment invoicePayment) {
this(invoicePayment.getId(), invoicePayment.getCreatedDate(), invoicePayment.getType(), invoicePayment.getInvoiceId(), invoicePayment.getPaymentId(),
invoicePayment.getPaymentDate(), invoicePayment.getAmount(), invoicePayment.getCurrency(), invoicePayment.getProcessedCurrency(), invoicePayment.getPaymentCookieId(),
- invoicePayment.getLinkedInvoicePaymentId());
+ invoicePayment.getLinkedInvoicePaymentId(), invoicePayment.isSuccess());
}
public InvoicePaymentType getType() {
@@ -136,6 +138,14 @@ public class InvoicePaymentModelDao extends EntityModelDaoBase implements Entity
this.linkedInvoicePaymentId = linkedInvoicePaymentId;
}
+ public Boolean getSuccess() {
+ return success;
+ }
+
+ public void setSuccess(final Boolean success) {
+ this.success = success;
+ }
+
@Override
public String toString() {
final StringBuilder sb = new StringBuilder();
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/dao/InvoicePaymentSqlDao.java b/invoice/src/main/java/org/killbill/billing/invoice/dao/InvoicePaymentSqlDao.java
index 626dda4..279aea3 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/dao/InvoicePaymentSqlDao.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/dao/InvoicePaymentSqlDao.java
@@ -19,16 +19,19 @@
package org.killbill.billing.invoice.dao;
import java.math.BigDecimal;
+import java.util.Date;
import java.util.List;
import java.util.UUID;
import org.killbill.billing.callcontext.InternalTenantContext;
+import org.killbill.billing.catalog.api.Currency;
import org.killbill.billing.invoice.api.InvoicePayment;
import org.killbill.billing.util.entity.dao.EntitySqlDao;
import org.killbill.billing.util.entity.dao.EntitySqlDaoStringTemplate;
import org.skife.jdbi.v2.sqlobject.Bind;
import org.skife.jdbi.v2.sqlobject.BindBean;
import org.skife.jdbi.v2.sqlobject.SqlQuery;
+import org.skife.jdbi.v2.sqlobject.SqlUpdate;
@EntitySqlDaoStringTemplate
public interface InvoicePaymentSqlDao extends EntitySqlDao<InvoicePaymentModelDao, InvoicePayment> {
@@ -64,4 +67,14 @@ public interface InvoicePaymentSqlDao extends EntitySqlDao<InvoicePaymentModelDa
@SqlQuery
List<InvoicePaymentModelDao> getChargebacksByPaymentId(@Bind("paymentId") final String paymentId,
@BindBean final InternalTenantContext context);
+
+
+
+ @SqlUpdate
+ void updateAttempt(@Bind("recordId") Long recordId,
+ @Bind("paymentDate") final Date paymentDate,
+ @Bind("amount") final BigDecimal amount,
+ @Bind("currency") final Currency currency,
+ @Bind("processedCurrency") final Currency processedCurrency,
+ @BindBean final InternalTenantContext context);
}
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/model/DefaultInvoicePayment.java b/invoice/src/main/java/org/killbill/billing/invoice/model/DefaultInvoicePayment.java
index abb8673..54363ab 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/model/DefaultInvoicePayment.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/model/DefaultInvoicePayment.java
@@ -41,21 +41,22 @@ public class DefaultInvoicePayment extends EntityBase implements InvoicePayment
private final Currency processedCurrency;
private final String paymentCookieId;
private final UUID linkedInvoicePaymentId;
+ private final Boolean isSuccess;
public DefaultInvoicePayment(final InvoicePaymentType type, final UUID paymentId, final UUID invoiceId, final DateTime paymentDate,
- final BigDecimal amount, final Currency currency, final Currency processedCurrency) {
- this(UUIDs.randomUUID(), null, type, paymentId, invoiceId, paymentDate, amount, currency, processedCurrency, null, null);
+ final BigDecimal amount, final Currency currency, final Currency processedCurrency, final Boolean isSuccess) {
+ this(UUIDs.randomUUID(), null, type, paymentId, invoiceId, paymentDate, amount, currency, processedCurrency, null, null, isSuccess);
}
public DefaultInvoicePayment(final UUID id, final InvoicePaymentType type, final UUID paymentId, final UUID invoiceId, final DateTime paymentDate,
@Nullable final BigDecimal amount, @Nullable final Currency currency, @Nullable final Currency processedCurrency, @Nullable final String paymentCookieId,
@Nullable final UUID linkedInvoicePaymentId) {
- this(id, null, type, paymentId, invoiceId, paymentDate, amount, currency, processedCurrency, paymentCookieId, linkedInvoicePaymentId);
+ this(id, null, type, paymentId, invoiceId, paymentDate, amount, currency, processedCurrency, paymentCookieId, linkedInvoicePaymentId, true);
}
public DefaultInvoicePayment(final UUID id, @Nullable final DateTime createdDate, final InvoicePaymentType type, final UUID paymentId, final UUID invoiceId, final DateTime paymentDate,
@Nullable final BigDecimal amount, @Nullable final Currency currency, @Nullable final Currency processedCurrency, @Nullable final String paymentCookieId,
- @Nullable final UUID linkedInvoicePaymentId) {
+ @Nullable final UUID linkedInvoicePaymentId, final Boolean isSuccess) {
super(id, createdDate, createdDate);
this.type = type;
this.paymentId = paymentId;
@@ -66,6 +67,7 @@ public class DefaultInvoicePayment extends EntityBase implements InvoicePayment
this.processedCurrency =processedCurrency;
this.paymentCookieId = paymentCookieId;
this.linkedInvoicePaymentId = linkedInvoicePaymentId;
+ this.isSuccess = isSuccess;
}
public DefaultInvoicePayment(final InvoicePaymentModelDao invoicePaymentModelDao) {
@@ -73,7 +75,8 @@ public class DefaultInvoicePayment extends EntityBase implements InvoicePayment
invoicePaymentModelDao.getPaymentId(), invoicePaymentModelDao.getInvoiceId(), invoicePaymentModelDao.getPaymentDate(),
invoicePaymentModelDao.getAmount(), invoicePaymentModelDao.getCurrency(), invoicePaymentModelDao.getProcessedCurrency(),
invoicePaymentModelDao.getPaymentCookieId(),
- invoicePaymentModelDao.getLinkedInvoicePaymentId());
+ invoicePaymentModelDao.getLinkedInvoicePaymentId(),
+ invoicePaymentModelDao.getSuccess());
}
@Override
@@ -121,4 +124,9 @@ public class DefaultInvoicePayment extends EntityBase implements InvoicePayment
return processedCurrency;
}
+ @Override
+ public Boolean isSuccess() {
+ return isSuccess;
+ }
+
}
diff --git a/invoice/src/main/resources/org/killbill/billing/invoice/dao/InvoicePaymentSqlDao.sql.stg b/invoice/src/main/resources/org/killbill/billing/invoice/dao/InvoicePaymentSqlDao.sql.stg
index fe0410f..b9e4dab 100644
--- a/invoice/src/main/resources/org/killbill/billing/invoice/dao/InvoicePaymentSqlDao.sql.stg
+++ b/invoice/src/main/resources/org/killbill/billing/invoice/dao/InvoicePaymentSqlDao.sql.stg
@@ -12,6 +12,7 @@ tableFields(prefix) ::= <<
, <prefix>processed_currency
, <prefix>payment_cookie_id
, <prefix>linked_invoice_payment_id
+, <prefix>success
, <prefix>created_by
, <prefix>created_date
>>
@@ -26,6 +27,7 @@ tableValues() ::= <<
, :processedCurrency
, :paymentCookieId
, :linkedInvoicePaymentId
+, :success
, :createdBy
, :createdDate
>>
@@ -70,6 +72,7 @@ getRemainingAmountPaid() ::= <<
SELECT SUM(amount)
FROM <tableName()>
WHERE (id = :invoicePaymentId OR linked_invoice_payment_id = :invoicePaymentId)
+ AND success
<AND_CHECK_TENANT()>
;
>>
@@ -103,3 +106,14 @@ getChargebacksByPaymentId() ::= <<
;
>>
+
+updateAttempt() ::= <<
+ UPDATE <tableName()>
+ SET success = true,
+ payment_date = :paymentDate,
+ amount = :amount,
+ processed_currency = :processedCurrency
+ WHERE record_id = :recordId
+ <AND_CHECK_TENANT("")>
+ ;
+>>
diff --git a/invoice/src/main/resources/org/killbill/billing/invoice/ddl.sql b/invoice/src/main/resources/org/killbill/billing/invoice/ddl.sql
index f8b1375..bb1ed4e 100644
--- a/invoice/src/main/resources/org/killbill/billing/invoice/ddl.sql
+++ b/invoice/src/main/resources/org/killbill/billing/invoice/ddl.sql
@@ -63,6 +63,7 @@ CREATE TABLE invoice_payments (
processed_currency varchar(3) NOT NULL,
payment_cookie_id varchar(255) DEFAULT NULL,
linked_invoice_payment_id varchar(36) DEFAULT NULL,
+ success bool DEFAULT true,
created_by varchar(50) NOT NULL,
created_date datetime NOT NULL,
account_record_id bigint /*! unsigned */ not null,
diff --git a/invoice/src/test/java/org/killbill/billing/invoice/dao/TestInvoiceDao.java b/invoice/src/test/java/org/killbill/billing/invoice/dao/TestInvoiceDao.java
index 0823824..8667bb3 100644
--- a/invoice/src/test/java/org/killbill/billing/invoice/dao/TestInvoiceDao.java
+++ b/invoice/src/test/java/org/killbill/billing/invoice/dao/TestInvoiceDao.java
@@ -150,7 +150,7 @@ public class TestInvoiceDao extends InvoiceTestSuiteWithEmbeddedDB {
final BigDecimal paymentAmount = new BigDecimal("11.00");
final UUID paymentId = UUID.randomUUID();
- final DefaultInvoicePayment defaultInvoicePayment = new DefaultInvoicePayment(InvoicePaymentType.ATTEMPT, paymentId, invoiceId, clock.getUTCNow().plusDays(12), paymentAmount, Currency.USD, Currency.USD);
+ final DefaultInvoicePayment defaultInvoicePayment = new DefaultInvoicePayment(InvoicePaymentType.ATTEMPT, paymentId, invoiceId, clock.getUTCNow().plusDays(12), paymentAmount, Currency.USD, Currency.USD, true);
invoiceDao.notifyOfPayment(new InvoicePaymentModelDao(defaultInvoicePayment), context);
final InvoiceModelDao retrievedInvoice = invoiceDao.getById(invoiceId, context);
@@ -521,7 +521,7 @@ public class TestInvoiceDao extends InvoiceTestSuiteWithEmbeddedDB {
invoiceUtil.createInvoiceItem(item2, context);
final BigDecimal payment1 = new BigDecimal("48.0");
- final InvoicePayment payment = new DefaultInvoicePayment(InvoicePaymentType.ATTEMPT, UUID.randomUUID(), invoice1.getId(), new DateTime(), payment1, Currency.USD, Currency.USD);
+ final InvoicePayment payment = new DefaultInvoicePayment(InvoicePaymentType.ATTEMPT, UUID.randomUUID(), invoice1.getId(), new DateTime(), payment1, Currency.USD, Currency.USD, true);
invoiceUtil.createPayment(payment, context);
final BigDecimal balance = invoiceDao.getAccountBalance(accountId, context);
@@ -586,7 +586,7 @@ public class TestInvoiceDao extends InvoiceTestSuiteWithEmbeddedDB {
invoiceUtil.createInvoice(invoice1, true, context);
final BigDecimal payment1 = new BigDecimal("48.0");
- final InvoicePayment payment = new DefaultInvoicePayment(InvoicePaymentType.ATTEMPT, UUID.randomUUID(), invoice1.getId(), new DateTime(), payment1, Currency.USD, Currency.USD);
+ final InvoicePayment payment = new DefaultInvoicePayment(InvoicePaymentType.ATTEMPT, UUID.randomUUID(), invoice1.getId(), new DateTime(), payment1, Currency.USD, Currency.USD, true);
invoiceUtil.createPayment(payment, context);
final BigDecimal balance = invoiceDao.getAccountBalance(accountId, context);
@@ -627,7 +627,7 @@ public class TestInvoiceDao extends InvoiceTestSuiteWithEmbeddedDB {
// Pay the whole thing
final UUID paymentId = UUID.randomUUID();
final BigDecimal payment1 = rate1;
- final InvoicePayment payment = new DefaultInvoicePayment(InvoicePaymentType.ATTEMPT, paymentId, invoice1.getId(), new DateTime(), payment1, Currency.USD, Currency.USD);
+ final InvoicePayment payment = new DefaultInvoicePayment(InvoicePaymentType.ATTEMPT, paymentId, invoice1.getId(), new DateTime(), payment1, Currency.USD, Currency.USD, true);
invoiceUtil.createPayment(payment, context);
balance = invoiceDao.getAccountBalance(accountId, context);
assertEquals(balance.compareTo(new BigDecimal("0.00")), 0);
@@ -676,7 +676,7 @@ public class TestInvoiceDao extends InvoiceTestSuiteWithEmbeddedDB {
// Pay the whole thing
final UUID paymentId = UUID.randomUUID();
final BigDecimal payment1 = amount;
- final InvoicePayment payment = new DefaultInvoicePayment(InvoicePaymentType.ATTEMPT, paymentId, invoice.getId(), new DateTime(), payment1, Currency.USD, Currency.USD);
+ final InvoicePayment payment = new DefaultInvoicePayment(InvoicePaymentType.ATTEMPT, paymentId, invoice.getId(), new DateTime(), payment1, Currency.USD, Currency.USD, true);
invoiceUtil.createPayment(payment, context);
balancePriorRefund = invoiceDao.getAccountBalance(accountId, context);
assertEquals(balancePriorRefund.compareTo(new BigDecimal("0.00")), 0);
@@ -781,7 +781,7 @@ public class TestInvoiceDao extends InvoiceTestSuiteWithEmbeddedDB {
// Pay the whole thing
final UUID paymentId = UUID.randomUUID();
final BigDecimal payment1 = amount1.add(rate1);
- final InvoicePayment payment = new DefaultInvoicePayment(InvoicePaymentType.ATTEMPT, paymentId, invoice1.getId(), new DateTime(), payment1, Currency.USD, Currency.USD);
+ final InvoicePayment payment = new DefaultInvoicePayment(InvoicePaymentType.ATTEMPT, paymentId, invoice1.getId(), new DateTime(), payment1, Currency.USD, Currency.USD, true);
invoiceUtil.createPayment(payment, context);
balance = invoiceDao.getAccountBalance(accountId, context);
assertEquals(balance.compareTo(new BigDecimal("0.00")), 0);
@@ -872,7 +872,7 @@ public class TestInvoiceDao extends InvoiceTestSuiteWithEmbeddedDB {
// Pay the whole thing
final BigDecimal payment1 = amount1.add(rate1);
- final InvoicePayment payment = new DefaultInvoicePayment(InvoicePaymentType.ATTEMPT, UUID.randomUUID(), invoice1.getId(), new DateTime(), payment1, Currency.USD, Currency.USD);
+ final InvoicePayment payment = new DefaultInvoicePayment(InvoicePaymentType.ATTEMPT, UUID.randomUUID(), invoice1.getId(), new DateTime(), payment1, Currency.USD, Currency.USD, true);
invoiceUtil.createPayment(payment, context);
balance = invoiceDao.getAccountBalance(accountId, context);
assertEquals(balance.compareTo(new BigDecimal("0.00")), 0);
@@ -1322,7 +1322,7 @@ public class TestInvoiceDao extends InvoiceTestSuiteWithEmbeddedDB {
// SECOND CREATE THE PAYMENT
final BigDecimal paymentAmount = new BigDecimal("239.00");
final UUID paymentId = UUID.randomUUID();
- final DefaultInvoicePayment defaultInvoicePayment = new DefaultInvoicePayment(InvoicePaymentType.ATTEMPT, paymentId, invoiceId, clock.getUTCNow(), paymentAmount, Currency.USD, Currency.USD);
+ final DefaultInvoicePayment defaultInvoicePayment = new DefaultInvoicePayment(InvoicePaymentType.ATTEMPT, paymentId, invoiceId, clock.getUTCNow(), paymentAmount, Currency.USD, Currency.USD, true);
invoiceDao.notifyOfPayment(new InvoicePaymentModelDao(defaultInvoicePayment), context);
// AND THEN THIRD THE REFUND
@@ -1467,7 +1467,7 @@ public class TestInvoiceDao extends InvoiceTestSuiteWithEmbeddedDB {
final UUID paymentId = UUID.randomUUID();
final DefaultInvoicePayment defaultInvoicePayment = new DefaultInvoicePayment(InvoicePaymentType.ATTEMPT, paymentId, invoice1.getId(), clock.getUTCNow().plusDays(12), new BigDecimal("10.0"),
- Currency.USD, Currency.USD);
+ Currency.USD, Currency.USD, true);
invoiceDao.notifyOfPayment(new InvoicePaymentModelDao(defaultInvoicePayment), context);
@@ -1533,7 +1533,7 @@ public class TestInvoiceDao extends InvoiceTestSuiteWithEmbeddedDB {
final UUID paymentId = UUID.randomUUID();
final DefaultInvoicePayment defaultInvoicePayment = new DefaultInvoicePayment(InvoicePaymentType.ATTEMPT, paymentId, invoice1.getId(), clock.getUTCNow().plusDays(12), paymentAmount,
- Currency.USD, Currency.USD);
+ Currency.USD, Currency.USD, true);
invoiceDao.notifyOfPayment(new InvoicePaymentModelDao(defaultInvoicePayment), context);
// Create invoice 2
@@ -1616,6 +1616,40 @@ public class TestInvoiceDao extends InvoiceTestSuiteWithEmbeddedDB {
invoiceUtil.verifyInvoice(invoice1.getId(), 0.00, 10.00, context);
}
+
+ @Test(groups = "slow")
+ public void testWithFailedPaymentAttempt() throws Exception {
+ final UUID accountId = account.getId();
+ final Invoice invoice = new DefaultInvoice(accountId, clock.getUTCToday(), clock.getUTCToday(), Currency.USD);
+ invoiceUtil.createInvoice(invoice, true, context);
+
+ final UUID bundleId = UUID.randomUUID();
+ final UUID subscriptionId = UUID.randomUUID();
+ final RecurringInvoiceItem item1 = new RecurringInvoiceItem(invoice.getId(), accountId, bundleId, subscriptionId, "test plan", "test ZOO", clock.getUTCNow().plusMonths(-1).toLocalDate(), clock.getUTCNow().toLocalDate(),
+ BigDecimal.TEN, BigDecimal.TEN, Currency.USD);
+ invoiceUtil.createInvoiceItem(item1, context);
+
+ final InvoiceModelDao retrievedInvoice = invoiceDao.getById(invoice.getId(), context);
+ assertEquals(retrievedInvoice.getInvoicePayments().size(), 0);
+
+
+ final UUID paymentId = UUID.randomUUID();
+ final DefaultInvoicePayment defaultInvoicePayment = new DefaultInvoicePayment(InvoicePaymentType.ATTEMPT, paymentId, invoice.getId(), clock.getUTCNow().plusDays(12), BigDecimal.TEN, Currency.USD, Currency.USD, false);
+ invoiceDao.notifyOfPayment(new InvoicePaymentModelDao(defaultInvoicePayment), context);
+
+ final InvoiceModelDao retrievedInvoice1 = invoiceDao.getById(invoice.getId(), context);
+ assertEquals(retrievedInvoice1.getInvoicePayments().size(), 1);
+ assertEquals(retrievedInvoice1.getInvoicePayments().get(0).getSuccess(), Boolean.FALSE);
+
+ final DefaultInvoicePayment defaultInvoicePayment2 = new DefaultInvoicePayment(InvoicePaymentType.ATTEMPT, paymentId, invoice.getId(), clock.getUTCNow().plusDays(12), BigDecimal.TEN, Currency.USD, Currency.USD, true);
+ invoiceDao.notifyOfPayment(new InvoicePaymentModelDao(defaultInvoicePayment2), context);
+
+ final InvoiceModelDao retrievedInvoice2 = invoiceDao.getById(invoice.getId(), context);
+ assertEquals(retrievedInvoice2.getInvoicePayments().size(), 1);
+ assertEquals(retrievedInvoice2.getInvoicePayments().get(0).getSuccess(), Boolean.TRUE);
+ }
+
+
private void createCredit(final UUID accountId, final LocalDate effectiveDate, final BigDecimal creditAmount) {
createCredit(accountId, null, effectiveDate, creditAmount);
}
diff --git a/invoice/src/test/java/org/killbill/billing/invoice/generator/TestDefaultInvoiceGenerator.java b/invoice/src/test/java/org/killbill/billing/invoice/generator/TestDefaultInvoiceGenerator.java
index 4882cc2..7cd34ee 100644
--- a/invoice/src/test/java/org/killbill/billing/invoice/generator/TestDefaultInvoiceGenerator.java
+++ b/invoice/src/test/java/org/killbill/billing/invoice/generator/TestDefaultInvoiceGenerator.java
@@ -918,7 +918,7 @@ public class TestDefaultInvoiceGenerator extends InvoiceTestSuiteNoDB {
// pay the invoice
invoice1.addPayment(new DefaultInvoicePayment(InvoicePaymentType.ATTEMPT, UUID.randomUUID(), invoice1.getId(), april25.toDateTimeAtCurrentTime(), TEN,
- Currency.USD, Currency.USD));
+ Currency.USD, Currency.USD, true));
assertEquals(invoice1.getBalance().compareTo(ZERO), 0);
// change the plan (i.e. repair) on start date
diff --git a/invoice/src/test/java/org/killbill/billing/invoice/proRations/InvoiceTestUtils.java b/invoice/src/test/java/org/killbill/billing/invoice/proRations/InvoiceTestUtils.java
index 1833d6b..bd3103c 100644
--- a/invoice/src/test/java/org/killbill/billing/invoice/proRations/InvoiceTestUtils.java
+++ b/invoice/src/test/java/org/killbill/billing/invoice/proRations/InvoiceTestUtils.java
@@ -134,6 +134,7 @@ public class InvoiceTestUtils {
Mockito.when(payment.getAmount()).thenReturn(amount);
Mockito.when(payment.getCurrency()).thenReturn(currency);
Mockito.when(payment.getProcessedCurrency()).thenReturn(currency);
+ Mockito.when(payment.isSuccess()).thenReturn(true);
invoicePaymentApi.notifyOfPayment(payment, callContext);
diff --git a/invoice/src/test/java/org/killbill/billing/invoice/template/formatters/TestDefaultInvoiceFormatter.java b/invoice/src/test/java/org/killbill/billing/invoice/template/formatters/TestDefaultInvoiceFormatter.java
index 5e18596..df0a9d7 100644
--- a/invoice/src/test/java/org/killbill/billing/invoice/template/formatters/TestDefaultInvoiceFormatter.java
+++ b/invoice/src/test/java/org/killbill/billing/invoice/template/formatters/TestDefaultInvoiceFormatter.java
@@ -134,9 +134,9 @@ public class TestDefaultInvoiceFormatter extends InvoiceTestSuiteNoDB {
invoice.addInvoiceItem(creditBalanceAdjInvoiceItem2);
invoice.addInvoiceItem(refundAdjInvoiceItem);
invoice.addPayment(new DefaultInvoicePayment(InvoicePaymentType.ATTEMPT, UUID.randomUUID(), invoice.getId(), clock.getUTCNow(), BigDecimal.TEN,
- Currency.USD, Currency.USD));
+ Currency.USD, Currency.USD, true));
invoice.addPayment(new DefaultInvoicePayment(InvoicePaymentType.REFUND, UUID.randomUUID(), invoice.getId(), clock.getUTCNow(), BigDecimal.ONE.negate(),
- Currency.USD, Currency.USD));
+ Currency.USD, Currency.USD, true));
// Check the scenario
Assert.assertEquals(invoice.getBalance().doubleValue(), 0.00);
Assert.assertEquals(invoice.getCreditedAmount().doubleValue(), 11.00);
diff --git a/payment/src/main/java/org/killbill/billing/payment/core/sm/control/OperationControlCallback.java b/payment/src/main/java/org/killbill/billing/payment/core/sm/control/OperationControlCallback.java
index add2adf..025bef2 100644
--- a/payment/src/main/java/org/killbill/billing/payment/core/sm/control/OperationControlCallback.java
+++ b/payment/src/main/java/org/killbill/billing/payment/core/sm/control/OperationControlCallback.java
@@ -17,6 +17,7 @@
package org.killbill.billing.payment.core.sm.control;
+import java.math.BigDecimal;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeoutException;
@@ -117,35 +118,34 @@ public abstract class OperationControlCallback extends OperationCallbackBase<Pay
final PaymentTransaction transaction = ((PaymentStateControlContext) paymentStateContext).getCurrentTransaction();
success = transaction.getTransactionStatus() == TransactionStatus.SUCCESS || transaction.getTransactionStatus() == TransactionStatus.PENDING;
+ final PaymentControlContext updatedPaymentControlContext = new DefaultPaymentControlContext(paymentStateContext.getAccount(),
+ paymentStateContext.getPaymentMethodId(),
+ paymentStateControlContext.getAttemptId(),
+ result.getId(),
+ result.getExternalKey(),
+ transaction.getId(),
+ paymentStateContext.getPaymentTransactionExternalKey(),
+ PaymentApiType.PAYMENT_TRANSACTION,
+ paymentStateContext.getTransactionType(),
+ null,
+ transaction.getAmount(),
+ transaction.getCurrency(),
+ transaction.getProcessedAmount(),
+ transaction.getProcessedCurrency(),
+ paymentStateControlContext.isApiPayment(),
+ paymentStateContext.getCallContext());
if (success) {
- final PaymentControlContext updatedPaymentControlContext = new DefaultPaymentControlContext(paymentStateContext.getAccount(),
- paymentStateContext.getPaymentMethodId(),
- paymentStateControlContext.getAttemptId(),
- result.getId(),
- result.getExternalKey(),
- transaction.getId(),
- paymentStateContext.getPaymentTransactionExternalKey(),
- PaymentApiType.PAYMENT_TRANSACTION,
- paymentStateContext.getTransactionType(),
- null,
- transaction.getAmount(),
- transaction.getCurrency(),
- transaction.getProcessedAmount(),
- transaction.getProcessedCurrency(),
- paymentStateControlContext.isApiPayment(),
- paymentStateContext.getCallContext());
-
executePluginOnSuccessCalls(paymentStateControlContext.getPaymentControlPluginNames(), updatedPaymentControlContext);
return PluginDispatcher.createPluginDispatcherReturnType(OperationResult.SUCCESS);
} else {
- throw new OperationException(null, executePluginOnFailureCallsAndSetRetryDate(paymentStateControlContext, paymentControlContext));
+ throw new OperationException(null, executePluginOnFailureCallsAndSetRetryDate(updatedPaymentControlContext));
}
} catch (final PaymentApiException e) {
// Wrap PaymentApiException, and throw a new OperationException with an ABORTED/FAILURE state based on the retry result.
- throw new OperationException(e, executePluginOnFailureCallsAndSetRetryDate(paymentStateControlContext, paymentControlContext));
+ throw new OperationException(e, executePluginOnFailureCallsAndSetRetryDate(paymentControlContext));
} catch (final RuntimeException e) {
// Attempts to set the retry date in context if needed.
- executePluginOnFailureCallsAndSetRetryDate(paymentStateControlContext, paymentControlContext);
+ executePluginOnFailureCallsAndSetRetryDate(paymentControlContext);
throw e;
}
}
@@ -202,7 +202,7 @@ public abstract class OperationControlCallback extends OperationCallbackBase<Pay
}
protected void executePluginOnSuccessCalls(final List<String> paymentControlPluginNames, final PaymentControlContext paymentControlContext) {
- // Values that were obtained/chnaged after the payment call was made (paymentId, processedAmount, processedCurrency,... needs to be extracted from the paymentControlContext)
+ // Values that were obtained/changed after the payment call was made (paymentId, processedAmount, processedCurrency,... needs to be extracted from the paymentControlContext)
// paymentId, paymentExternalKey, transactionAmount, transaction currency are extracted from paymentControlContext which was update from the operation result.
final OnSuccessPaymentControlResult result = controlPluginRunner.executePluginOnSuccessCalls(paymentStateContext.getAccount(),
paymentStateContext.getPaymentMethodId(),
@@ -225,7 +225,7 @@ public abstract class OperationControlCallback extends OperationCallbackBase<Pay
adjustStateContextPluginProperties(paymentStateContext, result.getAdjustedPluginProperties());
}
- private OperationResult executePluginOnFailureCallsAndSetRetryDate(final PaymentStateControlContext paymentStateControlContext, final PaymentControlContext paymentControlContext) {
+ private OperationResult executePluginOnFailureCallsAndSetRetryDate(final PaymentControlContext paymentControlContext) {
final DateTime retryDate = executePluginOnFailureCalls(paymentStateControlContext.getPaymentControlPluginNames(), paymentControlContext);
if (retryDate != null) {
((PaymentStateControlContext) paymentStateContext).setRetryDate(retryDate);
@@ -238,11 +238,11 @@ public abstract class OperationControlCallback extends OperationCallbackBase<Pay
final OnFailurePaymentControlResult result = controlPluginRunner.executePluginOnFailureCalls(paymentStateContext.getAccount(),
paymentControlContext.getPaymentMethodId(),
paymentStateControlContext.getAttemptId(),
- paymentStateContext.getPaymentId(),
- paymentStateContext.getPaymentExternalKey(),
- paymentStateContext.getPaymentTransactionExternalKey(),
+ paymentControlContext.getPaymentId(),
+ paymentControlContext.getPaymentExternalKey(),
+ paymentControlContext.getTransactionExternalKey(),
PaymentApiType.PAYMENT_TRANSACTION,
- paymentStateContext.getTransactionType(),
+ paymentControlContext.getTransactionType(),
null,
paymentControlContext.getAmount(),
paymentControlContext.getCurrency(),
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 8510301..1ea86bd 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
@@ -149,7 +149,7 @@ public final class InvoicePaymentControlPluginApi implements PaymentControlPlugi
case PURCHASE:
final UUID invoiceId = getInvoiceId(pluginProperties);
existingInvoicePayment = invoiceApi.getInvoicePaymentForAttempt(paymentControlContext.getPaymentId(), internalContext);
- if (existingInvoicePayment != null) {
+ if (existingInvoicePayment != null && existingInvoicePayment.isSuccess()) {
log.info("onSuccessCall was already completed for payment purchase :" + paymentControlContext.getPaymentId());
} else {
invoiceApi.notifyOfPayment(invoiceId,
@@ -158,6 +158,7 @@ public final class InvoicePaymentControlPluginApi implements PaymentControlPlugi
paymentControlContext.getProcessedCurrency(),
paymentControlContext.getPaymentId(),
paymentControlContext.getCreatedDate(),
+ true,
internalContext);
}
break;
@@ -193,11 +194,28 @@ public final class InvoicePaymentControlPluginApi implements PaymentControlPlugi
}
@Override
- public OnFailurePaymentControlResult onFailureCall(final PaymentControlContext paymentControlContext, final Iterable<PluginProperty> properties) throws PaymentControlApiException {
+ public OnFailurePaymentControlResult onFailureCall(final PaymentControlContext paymentControlContext, final Iterable<PluginProperty> pluginProperties) throws PaymentControlApiException {
final InternalCallContext internalContext = internalCallContextFactory.createInternalCallContext(paymentControlContext.getAccountId(), paymentControlContext);
final TransactionType transactionType = paymentControlContext.getTransactionType();
switch (transactionType) {
case PURCHASE:
+ final UUID invoiceId = getInvoiceId(pluginProperties);
+ if (paymentControlContext.getPaymentId() != null) {
+ try {
+ invoiceApi.notifyOfPayment(invoiceId,
+ paymentControlContext.getAmount(),
+ paymentControlContext.getCurrency(),
+ // processed currency may be null so we use currency; processed currency will be updated if/when payment succeeds
+ paymentControlContext.getCurrency(),
+ paymentControlContext.getPaymentId(),
+ paymentControlContext.getCreatedDate(),
+ false,
+ internalContext);
+ } catch (InvoiceApiException e) {
+ log.error("InvoicePaymentControlPluginApi onFailureCall failed ton update invoice for attemptId = " + paymentControlContext.getAttemptPaymentId() + ", transactionType = " + transactionType, e);
+ }
+ }
+
final DateTime nextRetryDate = computeNextRetryDate(paymentControlContext.getPaymentExternalKey(), paymentControlContext.isApiPayment(), internalContext);
return new DefaultFailureCallResult(nextRetryDate);
case REFUND:
diff --git a/payment/src/test/java/org/killbill/billing/payment/api/TestPaymentApi.java b/payment/src/test/java/org/killbill/billing/payment/api/TestPaymentApi.java
index a5b2a5a..0960bad 100644
--- a/payment/src/test/java/org/killbill/billing/payment/api/TestPaymentApi.java
+++ b/payment/src/test/java/org/killbill/billing/payment/api/TestPaymentApi.java
@@ -43,6 +43,7 @@ import org.killbill.billing.payment.plugin.api.PaymentPluginStatus;
import org.killbill.billing.payment.provider.MockPaymentProviderPlugin;
import org.killbill.bus.api.PersistentBus.EventBusException;
import org.testng.Assert;
+import org.testng.annotations.BeforeClass;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
@@ -54,6 +55,8 @@ import static org.testng.Assert.assertTrue;
public class TestPaymentApi extends PaymentTestSuiteWithEmbeddedDB {
+ private MockPaymentProviderPlugin mockPaymentProviderPlugin;
+
final PaymentOptions INVOICE_PAYMENT = new PaymentOptions() {
@Override
public boolean isExternalPayment() {
@@ -68,9 +71,16 @@ public class TestPaymentApi extends PaymentTestSuiteWithEmbeddedDB {
private Account account;
+ @BeforeClass(groups = "slow")
+ protected void beforeClass() throws Exception {
+ super.beforeClass();
+ mockPaymentProviderPlugin = (MockPaymentProviderPlugin) registry.getServiceForName(MockPaymentProviderPlugin.PLUGIN_NAME);
+ }
+
@BeforeMethod(groups = "slow")
public void beforeMethod() throws Exception {
super.beforeMethod();
+ mockPaymentProviderPlugin.clear();
account = testHelper.createTestAccount("bobo@gmail.com", true);
}
@@ -241,7 +251,6 @@ public class TestPaymentApi extends PaymentTestSuiteWithEmbeddedDB {
requestedAmount,
new BigDecimal("1.0"),
Currency.USD));
-
final Payment payment = paymentApi.createPurchaseWithPaymentControl(account, account.getPaymentMethodId(), null, requestedAmount, Currency.USD, paymentExternalKey, transactionExternalKey,
createPropertiesForInvoice(invoice), INVOICE_PAYMENT, callContext);
@@ -272,6 +281,64 @@ public class TestPaymentApi extends PaymentTestSuiteWithEmbeddedDB {
assertEquals(attempts.size(), 1);
}
+
+
+ @Test(groups = "slow")
+ public void testCreateFailedPurchaseWithPaymentControl() throws PaymentApiException, InvoiceApiException, EventBusException {
+
+ final BigDecimal requestedAmount = BigDecimal.TEN;
+ final UUID subscriptionId = UUID.randomUUID();
+ final UUID bundleId = UUID.randomUUID();
+ final LocalDate now = clock.getUTCToday();
+
+ final Invoice invoice = testHelper.createTestInvoice(account, now, Currency.USD);
+
+ final String paymentExternalKey = invoice.getId().toString();
+ final String transactionExternalKey = "brrrrrr";
+
+ mockPaymentProviderPlugin.makeNextPaymentFailWithError();
+
+ invoice.addInvoiceItem(new MockRecurringInvoiceItem(invoice.getId(), account.getId(),
+ subscriptionId,
+ bundleId,
+ "test plan", "test phase", null,
+ now,
+ now.plusMonths(1),
+ requestedAmount,
+ new BigDecimal("1.0"),
+ Currency.USD));
+ try {
+ paymentApi.createPurchaseWithPaymentControl(account, account.getPaymentMethodId(), null, requestedAmount, Currency.USD, paymentExternalKey, transactionExternalKey,
+ createPropertiesForInvoice(invoice), INVOICE_PAYMENT, callContext);
+ } catch (final PaymentApiException expected) {
+ }
+
+
+ final List<Payment> accountPayments = paymentApi.getAccountPayments(account.getId(), false, ImmutableList.<PluginProperty>of(), callContext);
+ assertEquals(accountPayments.size(), 1);
+ final Payment payment = accountPayments.get(0);
+ assertEquals(payment.getExternalKey(), paymentExternalKey);
+ assertEquals(payment.getPaymentMethodId(), account.getPaymentMethodId());
+ assertEquals(payment.getAccountId(), account.getId());
+ assertEquals(payment.getAuthAmount().compareTo(BigDecimal.ZERO), 0);
+ assertEquals(payment.getCapturedAmount().compareTo(BigDecimal.ZERO), 0);
+ assertEquals(payment.getPurchasedAmount().compareTo(BigDecimal.ZERO), 0);
+ assertEquals(payment.getRefundedAmount().compareTo(BigDecimal.ZERO), 0);
+ assertEquals(payment.getCurrency(), Currency.USD);
+
+ assertEquals(payment.getTransactions().size(), 1);
+ assertEquals(payment.getTransactions().get(0).getExternalKey(), transactionExternalKey);
+ assertEquals(payment.getTransactions().get(0).getPaymentId(), payment.getId());
+ assertEquals(payment.getTransactions().get(0).getAmount().compareTo(requestedAmount), 0);
+ assertEquals(payment.getTransactions().get(0).getCurrency(), Currency.USD);
+ assertEquals(payment.getTransactions().get(0).getProcessedAmount().compareTo(requestedAmount), 0); // This is weird...
+ assertEquals(payment.getTransactions().get(0).getProcessedCurrency(), Currency.USD);
+
+ assertEquals(payment.getTransactions().get(0).getTransactionStatus(), TransactionStatus.PAYMENT_FAILURE);
+ assertEquals(payment.getTransactions().get(0).getTransactionType(), TransactionType.PURCHASE);
+ }
+
+
@Test(groups = "slow")
public void testCreateAbortedPurchaseWithPaymentControl() throws InvoiceApiException, EventBusException {
pom.xml 2(+1 -1)
diff --git a/pom.xml b/pom.xml
index 57731da..4aa4e0c 100644
--- a/pom.xml
+++ b/pom.xml
@@ -21,7 +21,7 @@
<parent>
<artifactId>killbill-oss-parent</artifactId>
<groupId>org.kill-bill.billing</groupId>
- <version>0.42</version>
+ <version>0.43</version>
</parent>
<artifactId>killbill</artifactId>
<version>0.15.4-SNAPSHOT</version>