killbill-aplcache
Changes
account/pom.xml 5(+5 -0)
account/src/test/java/org/killbill/billing/account/api/user/TestDefaultAccountUserApi.java 27(+27 -0)
beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestIntegrationInvoiceWithRepairLogic.java 7(+4 -3)
beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestPaymentWithControl.java 51(+43 -8)
entitlement/src/main/java/org/killbill/billing/entitlement/engine/core/EventsStreamBuilder.java 10(+5 -5)
entitlement/src/test/java/org/killbill/billing/entitlement/engine/core/TestEntitlementUtils.java 7(+3 -4)
invoice/src/main/java/org/killbill/billing/invoice/template/formatters/DefaultInvoiceItemFormatter.java 4(+2 -2)
junction/src/main/java/org/killbill/billing/junction/plumbing/billing/BlockingCalculator.java 13(+10 -3)
overdue/src/main/java/org/killbill/billing/overdue/applicator/formatters/DefaultBillingStateFormatter.java 4(+2 -2)
payment/src/main/java/org/killbill/billing/payment/core/janitor/IncompletePaymentAttemptTask.java 9(+3 -6)
payment/src/main/java/org/killbill/billing/payment/core/janitor/IncompletePaymentTransactionTask.java 36(+17 -19)
payment/src/main/java/org/killbill/billing/payment/core/PaymentPluginServiceRegistration.java 89(+89 -0)
payment/src/main/java/org/killbill/billing/payment/core/PluginControlPaymentProcessor.java 10(+4 -6)
payment/src/main/java/org/killbill/billing/payment/core/sm/control/AuthorizeControlOperation.java 2(+2 -0)
payment/src/main/java/org/killbill/billing/payment/core/sm/control/CaptureControlOperation.java 1(+1 -0)
payment/src/main/java/org/killbill/billing/payment/core/sm/control/ChargebackControlOperation.java 1(+1 -0)
payment/src/main/java/org/killbill/billing/payment/core/sm/control/ChargebackReversalControlOperation.java 1(+1 -0)
payment/src/main/java/org/killbill/billing/payment/core/sm/control/ControlPluginRunner.java 17(+16 -1)
payment/src/main/java/org/killbill/billing/payment/core/sm/control/CreditControlOperation.java 2(+2 -0)
payment/src/main/java/org/killbill/billing/payment/core/sm/control/DefaultControlInitiated.java 9(+7 -2)
payment/src/main/java/org/killbill/billing/payment/core/sm/control/PaymentStateControlContext.java 10(+9 -1)
payment/src/main/java/org/killbill/billing/payment/core/sm/control/PurchaseControlOperation.java 2(+2 -0)
payment/src/main/java/org/killbill/billing/payment/core/sm/control/RefundControlOperation.java 1(+1 -0)
payment/src/main/java/org/killbill/billing/payment/core/sm/control/VoidControlOperation.java 1(+1 -0)
payment/src/main/java/org/killbill/billing/payment/core/sm/PaymentAutomatonDAOHelper.java 86(+52 -34)
payment/src/main/java/org/killbill/billing/payment/core/sm/PluginControlPaymentAutomatonRunner.java 13(+6 -7)
payment/src/main/java/org/killbill/billing/payment/invoice/InvoicePaymentControlPluginApi.java 3(+1 -2)
payment/src/main/resources/org/killbill/billing/payment/migration/V20170302005722__enlarge_gateway_error_msg.sql 2(+2 -0)
payment/src/test/java/org/killbill/billing/payment/core/sm/MockRetryablePaymentAutomatonRunner.java 10(+5 -5)
payment/src/test/java/org/killbill/billing/payment/core/sm/TestPaymentAutomatonDAOHelper.java 17(+11 -6)
payment/src/test/java/org/killbill/billing/payment/core/sm/TestPaymentEnteringStateCallback.java 6(+3 -3)
payment/src/test/java/org/killbill/billing/payment/core/sm/TestPaymentLeavingStateCallback.java 10(+5 -5)
profiles/killbill/src/main/java/org/killbill/billing/server/log/obfuscators/PatternObfuscator.java 17(+17 -0)
profiles/killbill/src/test/java/org/killbill/billing/server/log/obfuscators/TestPatternObfuscator.java 6(+6 -0)
subscription/src/main/java/org/killbill/billing/subscription/engine/dao/model/SubscriptionBundleModelDao.java 4(+2 -2)
subscription/src/main/resources/org/killbill/billing/subscription/migration/V20161130055444__bundles_external_key_not_null.sql 0(+0 -0)
util/src/main/java/org/killbill/billing/util/audit/DefaultAccountAuditLogsForObjectType.java 3(+2 -1)
Details
account/pom.xml 5(+5 -0)
diff --git a/account/pom.xml b/account/pom.xml
index 78a4147..d27448c 100644
--- a/account/pom.xml
+++ b/account/pom.xml
@@ -120,6 +120,11 @@
</dependency>
<dependency>
<groupId>org.kill-bill.billing</groupId>
+ <artifactId>killbill-tenant</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.kill-bill.billing</groupId>
<artifactId>killbill-util</artifactId>
</dependency>
<dependency>
diff --git a/account/src/test/java/org/killbill/billing/account/api/user/TestDefaultAccountUserApi.java b/account/src/test/java/org/killbill/billing/account/api/user/TestDefaultAccountUserApi.java
index 0a0d2ac..9fe5b8c 100644
--- a/account/src/test/java/org/killbill/billing/account/api/user/TestDefaultAccountUserApi.java
+++ b/account/src/test/java/org/killbill/billing/account/api/user/TestDefaultAccountUserApi.java
@@ -33,8 +33,12 @@ import org.killbill.billing.account.api.DefaultAccount;
import org.killbill.billing.account.api.DefaultMutableAccountData;
import org.killbill.billing.account.api.MutableAccountData;
import org.killbill.billing.account.dao.AccountModelDao;
+import org.killbill.billing.callcontext.DefaultCallContext;
import org.killbill.billing.catalog.api.Currency;
import org.killbill.billing.events.AccountCreationInternalEvent;
+import org.killbill.billing.tenant.dao.TenantModelDao;
+import org.killbill.billing.tenant.dao.TenantSqlDao;
+import org.killbill.billing.util.callcontext.CallContext;
import org.killbill.billing.util.entity.Pagination;
import org.testng.Assert;
import org.testng.annotations.Test;
@@ -432,4 +436,27 @@ public class TestDefaultAccountUserApi extends AccountTestSuiteWithEmbeddedDB {
assertEquals(e.getCode(), ErrorCode.EXTERNAL_KEY_LIMIT_EXCEEDED.getCode());
}
}
+
+ @Test(groups = "slow", description = "Test Account creation with same External Key in different tenants")
+ public void testCreateAccountWithSameExternalKeyInDifferentTenants() throws Exception {
+ final AccountData accountData = createAccountData();
+
+ final Account account1 = accountUserApi.createAccount(accountData, callContext);
+ try {
+ // Same tenant
+ accountUserApi.createAccount(accountData, callContext);
+ Assert.fail();
+ } catch (final AccountApiException e) {
+ assertEquals(e.getCode(), ErrorCode.ACCOUNT_ALREADY_EXISTS.getCode());
+ }
+
+ final TenantSqlDao tenantSqlDao = dbi.onDemand(TenantSqlDao.class);
+ final TenantModelDao tenant2 = new TenantModelDao();
+ tenantSqlDao.create(tenant2, internalCallContext);
+ final CallContext callContext2 = new DefaultCallContext(tenant2.getId(), callContext.getUserName(), callContext.getCallOrigin(), callContext.getUserType(), callContext.getUserToken(), clock);
+ final Account account2 = accountUserApi.createAccount(accountData, callContext2);
+
+ Assert.assertEquals(account1.getExternalKey(), account2.getExternalKey());
+ Assert.assertNotEquals(account1.getId(), account2.getId());
+ }
}
diff --git a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestIntegration.java b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestIntegration.java
index fd03318..fa7cc6c 100644
--- a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestIntegration.java
+++ b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestIntegration.java
@@ -32,6 +32,7 @@ import org.killbill.billing.account.api.AccountData;
import org.killbill.billing.api.TestApiListener.NextEvent;
import org.killbill.billing.beatrix.util.InvoiceChecker.ExpectedInvoiceItemCheck;
import org.killbill.billing.beatrix.util.PaymentChecker.ExpectedPaymentCheck;
+import org.killbill.billing.catalog.api.BillingActionPolicy;
import org.killbill.billing.catalog.api.BillingPeriod;
import org.killbill.billing.catalog.api.Currency;
import org.killbill.billing.catalog.api.PriceListSet;
@@ -609,6 +610,106 @@ public class TestIntegration extends TestIntegrationBase {
checkNoMoreInvoiceToGenerate(account);
}
+ @Test(groups = "slow", description = "https://github.com/killbill/killbill/issues/715")
+ public void testWithPauseResumeAfterENT_CANCELLEDBlockingState() throws Exception {
+ final DateTime initialDate = new DateTime(2012, 2, 1, 0, 3, 42, 0, testTimeZone);
+ final int billingDay = 2;
+ // set clock to the initial start date
+ clock.setDeltaFromReality(initialDate.getMillis() - clock.getUTCNow().getMillis());
+
+ final Account account = createAccountWithNonOsgiPaymentMethod(getAccountData(billingDay));
+ final UUID accountId = account.getId();
+ assertNotNull(account);
+
+ final String productName = "Shotgun";
+ final BillingPeriod term = BillingPeriod.MONTHLY;
+
+ //
+ // CREATE SUBSCRIPTION AND EXPECT BOTH EVENTS: NextEvent.CREATE, NextEvent.BLOCK NextEvent.INVOICE
+ //
+ final DefaultEntitlement baseEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "bundleKey", productName, ProductCategory.BASE, term, NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE);
+ assertNotNull(baseEntitlement);
+
+ //
+ // VERIFY CTD HAS BEEN SET
+ //
+ final DefaultSubscriptionBase subscription = (DefaultSubscriptionBase) baseEntitlement.getSubscriptionBase();
+ final DateTime startDate = subscription.getCurrentPhaseStart();
+ final BigDecimal rate = subscription.getCurrentPhase().getFixed().getPrice().getPrice(Currency.USD);
+ verifyTestResult(accountId, subscription.getId(), startDate, null, rate, clock.getUTCNow(), 1);
+
+ //
+ // MOVE TIME TO AFTER TRIAL (2012-03-04)
+ //
+ busHandler.pushExpectedEvents(NextEvent.PHASE, NextEvent.INVOICE, NextEvent.INVOICE_PAYMENT, NextEvent.PAYMENT);
+ clock.addDeltaFromReality(AT_LEAST_ONE_MONTH_MS);
+ assertListenerStatus();
+
+ invoiceChecker.checkInvoice(accountId,
+ 1,
+ callContext,
+ ImmutableList.<ExpectedInvoiceItemCheck>of(new ExpectedInvoiceItemCheck(new LocalDate(2012, 2, 1), null, InvoiceItemType.FIXED, BigDecimal.ZERO)));
+
+ invoiceChecker.checkInvoice(accountId,
+ 2,
+ callContext,
+ ImmutableList.<ExpectedInvoiceItemCheck>of(new ExpectedInvoiceItemCheck(new LocalDate(2012, 3, 2), new LocalDate(2012, 4, 2), InvoiceItemType.RECURRING, new BigDecimal("249.95"))));
+
+ // Pause the entitlement between 2012-03-05 and 2012-03-15
+ DefaultEntitlement entitlement = (DefaultEntitlement) entitlementApi.getEntitlementForId(baseEntitlement.getId(), callContext);
+ entitlementApi.pause(entitlement.getBundleId(), new LocalDate(2012, 3, 5), ImmutableList.<PluginProperty>of(), callContext);
+ entitlementApi.resume(entitlement.getBundleId(), new LocalDate(2012, 3, 15), ImmutableList.<PluginProperty>of(), callContext);
+
+ // Advance clock to 2012-03-07
+ busHandler.pushExpectedEvents(NextEvent.BLOCK, NextEvent.INVOICE);
+ clock.addDays(3);
+ assertListenerStatus();
+
+ invoiceChecker.checkInvoice(accountId,
+ 2,
+ callContext,
+ ImmutableList.<ExpectedInvoiceItemCheck>of(new ExpectedInvoiceItemCheck(new LocalDate(2012, 3, 2), new LocalDate(2012, 4, 2), InvoiceItemType.RECURRING, new BigDecimal("249.95"))));
+
+ invoiceChecker.checkInvoice(accountId,
+ 3,
+ callContext,
+ ImmutableList.<ExpectedInvoiceItemCheck>of(new ExpectedInvoiceItemCheck(new LocalDate(2012, 3, 5), new LocalDate(2012, 4, 2), InvoiceItemType.REPAIR_ADJ, new BigDecimal("-225.76")),
+ new ExpectedInvoiceItemCheck(new LocalDate(2012, 3, 7), new LocalDate(2012, 3, 7), InvoiceItemType.CBA_ADJ, new BigDecimal("225.76"))));
+
+ // Entitlement should be blocked
+ entitlement = (DefaultEntitlement) entitlementApi.getEntitlementForId(baseEntitlement.getId(), callContext);
+ Assert.assertEquals(entitlement.getState(), EntitlementState.BLOCKED);
+
+ // Advance clock to 2012-03-12, nothing should happen
+ clock.addDays(5);
+ assertListenerStatus();
+
+ // Entitlement is still blocked
+ entitlement = (DefaultEntitlement) entitlementApi.getEntitlementForId(baseEntitlement.getId(), callContext);
+ Assert.assertEquals(entitlement.getState(), EntitlementState.BLOCKED);
+
+ List<Invoice> invoices = invoiceUserApi.getInvoicesByAccount(accountId, false, callContext);
+ assertEquals(invoices.size(), 3);
+
+ // Cancel entitlement start of term but with billing policy immediate (ENT_BLOCKED must be after ENT_CANCELLED to trigger the bug)
+ busHandler.pushExpectedEvents(NextEvent.BLOCK, NextEvent.CANCEL, NextEvent.NULL_INVOICE);
+ baseEntitlement.cancelEntitlementWithDateOverrideBillingPolicy(new LocalDate(2012, 3, 2), BillingActionPolicy.IMMEDIATE, ImmutableList.<PluginProperty>of(), callContext);
+ assertListenerStatus();
+
+ // 2012-03-16
+ busHandler.pushExpectedEvents(NextEvent.BLOCK, NextEvent.NULL_INVOICE);
+ clock.addDays(4);
+ assertListenerStatus();
+
+ busHandler.pushExpectedEvents(NextEvent.NULL_INVOICE);
+ clock.addMonths(3);
+ assertListenerStatus();
+
+ // No new invoices
+ invoices = invoiceUserApi.getInvoicesByAccount(accountId, false, callContext);
+ assertEquals(invoices.size(), 3);
+ }
+
@Test(groups = "slow")
public void testForMultipleRecurringPhases() throws Exception {
final DateTime initialCreationDate = new DateTime(2012, 2, 1, 0, 3, 42, 0, testTimeZone);
diff --git a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestIntegrationInvoiceWithRepairLogic.java b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestIntegrationInvoiceWithRepairLogic.java
index 8fd9f9d..18f2da2 100644
--- a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestIntegrationInvoiceWithRepairLogic.java
+++ b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestIntegrationInvoiceWithRepairLogic.java
@@ -1,7 +1,7 @@
/*
* Copyright 2010-2013 Ning, Inc.
- * Copyright 2014-2016 Groupon, Inc
- * Copyright 2014-2016 The Billing Project, LLC
+ * Copyright 2014-2017 Groupon, Inc
+ * Copyright 2014-2017 The Billing Project, LLC
*
* The Billing Project licenses this file to you under the Apache License, version 2.0
* (the "License"); you may not use this file except in compliance with the
@@ -727,7 +727,8 @@ public class TestIntegrationInvoiceWithRepairLogic extends TestIntegrationBase {
newItems.add(repair23);
newItems.add(recurring3);
newItems.add(repair3);
- invoiceDao.createInvoice(shellInvoice, newItems, false, new FutureAccountNotifications(new HashMap<UUID, List<SubscriptionNotification>>()), internalCallContext);
+ shellInvoice.addInvoiceItems(newItems);
+ invoiceDao.createInvoice(shellInvoice, new FutureAccountNotifications(new HashMap<UUID, List<SubscriptionNotification>>()), internalCallContext);
// Move ahead one month, verify nothing from previous data was generated
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 25c4902..6bce758 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
@@ -573,6 +573,9 @@ public class TestInvoicePayment extends TestIntegrationBase {
assertEquals(payments.get(0).getTransactions().get(0).getCurrency(), Currency.USD);
assertEquals(payments.get(0).getTransactions().get(0).getProcessedAmount().compareTo(BigDecimal.ZERO), 0);
assertEquals(payments.get(0).getTransactions().get(0).getProcessedCurrency(), Currency.USD);
+ // Verify fix for https://github.com/killbill/killbill/issues/349
+ assertEquals(payments.get(0).getId().toString(), payments.get(0).getExternalKey());
+ assertEquals(payments.get(0).getTransactions().get(0).getId().toString(), payments.get(0).getTransactions().get(0).getExternalKey());
// Trigger the payment retry
busHandler.pushExpectedEvents(NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
diff --git a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestPaymentWithControl.java b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestPaymentWithControl.java
index 05879f6..f5b74e6 100644
--- a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestPaymentWithControl.java
+++ b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestPaymentWithControl.java
@@ -30,18 +30,18 @@ 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.Currency;
-import org.killbill.billing.osgi.api.OSGIServiceDescriptor;
-import org.killbill.billing.osgi.api.OSGIServiceRegistration;
-import org.killbill.billing.payment.api.Payment;
-import org.killbill.billing.payment.api.PaymentOptions;
-import org.killbill.billing.payment.api.PluginProperty;
-import org.killbill.billing.payment.api.TransactionType;
import org.killbill.billing.control.plugin.api.OnFailurePaymentControlResult;
import org.killbill.billing.control.plugin.api.OnSuccessPaymentControlResult;
import org.killbill.billing.control.plugin.api.PaymentControlApiException;
import org.killbill.billing.control.plugin.api.PaymentControlContext;
import org.killbill.billing.control.plugin.api.PaymentControlPluginApi;
import org.killbill.billing.control.plugin.api.PriorPaymentControlResult;
+import org.killbill.billing.osgi.api.OSGIServiceDescriptor;
+import org.killbill.billing.osgi.api.OSGIServiceRegistration;
+import org.killbill.billing.payment.api.Payment;
+import org.killbill.billing.payment.api.PaymentOptions;
+import org.killbill.billing.payment.api.PluginProperty;
+import org.killbill.billing.payment.api.TransactionType;
import org.testng.Assert;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.BeforeMethod;
@@ -118,10 +118,23 @@ public class TestPaymentWithControl extends TestIntegrationBase {
properties, paymentOptions, callContext);
assertListenerStatus();
+ Payment paymentWithAttempts = paymentApi.getPayment(payment.getId(), false, true, ImmutableList.<PluginProperty>of(), callContext);
+ Assert.assertEquals(paymentWithAttempts.getPaymentAttempts().size(), 1);
+ Assert.assertEquals(paymentWithAttempts.getExternalKey(), paymentWithAttempts.getPaymentAttempts().get(0).getPaymentExternalKey());
+ Assert.assertEquals(paymentWithAttempts.getTransactions().get(0).getExternalKey(), paymentWithAttempts.getPaymentAttempts().get(0).getTransactionExternalKey());
+ // Verify fix for https://github.com/killbill/killbill/issues/349 when no external key is set
+ Assert.assertEquals(paymentWithAttempts.getId().toString(), paymentWithAttempts.getPaymentAttempts().get(0).getPaymentExternalKey());
+ Assert.assertEquals(paymentWithAttempts.getTransactions().get(0).getId().toString(), paymentWithAttempts.getPaymentAttempts().get(0).getTransactionExternalKey());
+
busHandler.pushExpectedEvents(NextEvent.PAYMENT);
paymentApi.createCaptureWithPaymentControl(account, payment.getId(), BigDecimal.ONE, account.getCurrency(), null, properties, paymentOptions, callContext);
assertListenerStatus();
+ paymentWithAttempts = paymentApi.getPayment(payment.getId(), false, true, ImmutableList.<PluginProperty>of(), callContext);
+ Assert.assertEquals(paymentWithAttempts.getPaymentAttempts().size(), 2);
+ Assert.assertEquals(paymentWithAttempts.getTransactions().get(1).getExternalKey(), paymentWithAttempts.getPaymentAttempts().get(1).getTransactionExternalKey());
+ Assert.assertEquals(paymentWithAttempts.getTransactions().get(1).getId().toString(), paymentWithAttempts.getPaymentAttempts().get(1).getTransactionExternalKey());
+
Assert.assertEquals(testPaymentControlWithControl.getCalls().size(), 2);
Assert.assertEquals(testPaymentControlWithControl.getCalls().get(TransactionType.AUTHORIZE.toString()), new Integer(1));
Assert.assertEquals(testPaymentControlWithControl.getCalls().get(TransactionType.CAPTURE.toString()), new Integer(1));
@@ -132,14 +145,36 @@ public class TestPaymentWithControl extends TestIntegrationBase {
final AccountData accountData = getAccountData(1);
final Account account = createAccountWithNonOsgiPaymentMethod(accountData);
+ final String paymentExternalKey = "something-that-is-not-a-uuid";
+ final String paymentTransactionExternalKey = "something-that-is-not-a-uuid-2";
+
busHandler.pushExpectedEvents(NextEvent.PAYMENT);
- final Payment payment = paymentApi.createAuthorizationWithPaymentControl(account, account.getPaymentMethodId(), null, BigDecimal.ONE, account.getCurrency(), null, null,
+ final Payment payment = paymentApi.createAuthorizationWithPaymentControl(account, account.getPaymentMethodId(), null, BigDecimal.ONE, account.getCurrency(), paymentExternalKey, paymentTransactionExternalKey,
properties, paymentOptions, callContext);
assertListenerStatus();
+ Payment paymentWithAttempts = paymentApi.getPayment(payment.getId(), false, true, ImmutableList.<PluginProperty>of(), callContext);
+ Assert.assertEquals(paymentWithAttempts.getPaymentAttempts().size(), 1);
+ Assert.assertEquals(paymentWithAttempts.getExternalKey(), paymentExternalKey);
+ Assert.assertEquals(paymentWithAttempts.getTransactions().get(0).getExternalKey(), paymentTransactionExternalKey);
+ Assert.assertEquals(paymentWithAttempts.getExternalKey(), paymentWithAttempts.getPaymentAttempts().get(0).getPaymentExternalKey());
+ Assert.assertEquals(paymentWithAttempts.getTransactions().get(0).getExternalKey(), paymentWithAttempts.getPaymentAttempts().get(0).getTransactionExternalKey());
+ // Verify fix for https://github.com/killbill/killbill/issues/349 when external key is set
+ Assert.assertNotEquals(paymentWithAttempts.getId().toString(), paymentWithAttempts.getPaymentAttempts().get(0).getPaymentExternalKey());
+ Assert.assertNotEquals(paymentWithAttempts.getTransactions().get(0).getId().toString(), paymentWithAttempts.getPaymentAttempts().get(0).getTransactionExternalKey());
+
+ final String paymentTransactionExternalKey2 = "something-that-is-not-a-uuid-3";
+
busHandler.pushExpectedEvents(NextEvent.PAYMENT);
- paymentApi.createVoidWithPaymentControl(account, payment.getId(), null, properties, paymentOptions, callContext);
+ paymentApi.createVoidWithPaymentControl(account, payment.getId(), paymentTransactionExternalKey2, properties, paymentOptions, callContext);
assertListenerStatus();
+
+ paymentWithAttempts = paymentApi.getPayment(payment.getId(), false, true, ImmutableList.<PluginProperty>of(), callContext);
+ Assert.assertEquals(paymentWithAttempts.getPaymentAttempts().size(), 2);
+ Assert.assertEquals(paymentWithAttempts.getTransactions().get(1).getExternalKey(), paymentTransactionExternalKey2);
+ Assert.assertEquals(paymentWithAttempts.getTransactions().get(1).getExternalKey(), paymentWithAttempts.getPaymentAttempts().get(1).getTransactionExternalKey());
+ Assert.assertNotEquals(paymentWithAttempts.getTransactions().get(1).getId().toString(), paymentWithAttempts.getPaymentAttempts().get(1).getTransactionExternalKey());
+
Assert.assertEquals(testPaymentControlWithControl.getCalls().size(), 2);
Assert.assertEquals(testPaymentControlWithControl.getCalls().get(TransactionType.AUTHORIZE.toString()), new Integer(1));
Assert.assertEquals(testPaymentControlWithControl.getCalls().get(TransactionType.VOID.toString()), new Integer(1));
diff --git a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestWithTaxItems.java b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestWithTaxItems.java
index c0e2582..6d1b9df 100644
--- a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestWithTaxItems.java
+++ b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestWithTaxItems.java
@@ -34,6 +34,7 @@ import org.killbill.billing.catalog.api.BillingActionPolicy;
import org.killbill.billing.catalog.api.BillingPeriod;
import org.killbill.billing.catalog.api.ProductCategory;
import org.killbill.billing.entitlement.api.DefaultEntitlement;
+import org.killbill.billing.invoice.api.DryRunType;
import org.killbill.billing.invoice.api.Invoice;
import org.killbill.billing.invoice.api.InvoiceItem;
import org.killbill.billing.invoice.api.InvoiceItemType;
@@ -189,6 +190,53 @@ public class TestWithTaxItems extends TestIntegrationBase {
}
+ @Test(groups = "slow", description = "https://github.com/killbill/killbill/issues/637")
+ public void testDryRunTaxItemsWithCredits() throws Exception {
+ // We take april as it has 30 days (easier to play with BCD)
+ // Set clock to the initial start date - we implicitly assume here that the account timezone is UTC
+ clock.setDay(new LocalDate(2012, 4, 1));
+
+ final AccountData accountData = getAccountData(1);
+ final Account account = createAccountWithNonOsgiPaymentMethod(accountData);
+ accountChecker.checkAccount(account.getId(), accountData, callContext);
+
+ // Create original subscription (Trial PHASE) -> $0 invoice.
+ final DefaultEntitlement bpSubscription = createBaseEntitlementAndCheckForCompletion(account.getId(), "bundleKey", "Pistol", ProductCategory.BASE, BillingPeriod.MONTHLY, NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE);
+ invoiceChecker.checkInvoice(account.getId(), 1, callContext, new ExpectedInvoiceItemCheck(new LocalDate(2012, 4, 1), null, InvoiceItemType.FIXED, new BigDecimal("0")));
+ subscriptionChecker.checkSubscriptionCreated(bpSubscription.getId(), internalCallContext);
+
+ busHandler.pushExpectedEvents(NextEvent.INVOICE);
+ invoiceUserApi.insertCredit(account.getId(), new BigDecimal("100"), clock.getUTCToday(), account.getCurrency(), true, "VIP", callContext);
+ assertListenerStatus();
+
+ invoiceChecker.checkInvoice(account.getId(), 2, callContext,
+ new ExpectedInvoiceItemCheck(new LocalDate(2012, 4, 1), new LocalDate(2012, 4, 1), InvoiceItemType.CBA_ADJ, new BigDecimal("100")),
+ new ExpectedInvoiceItemCheck(new LocalDate(2012, 4, 1), new LocalDate(2012, 4, 1), InvoiceItemType.CREDIT_ADJ, new BigDecimal("-100")));
+
+ // Make sure TestInvoicePluginApi will return an additional TAX item
+ testInvoicePluginApi.addTaxItem();
+
+ // Verify dry-run scenario
+ final Invoice dryRunInvoice = invoiceUserApi.triggerInvoiceGeneration(account.getId(), new LocalDate(2012, 5, 1), new TestDryRunArguments(DryRunType.TARGET_DATE), callContext);
+ invoiceChecker.checkInvoiceNoAudits(dryRunInvoice, callContext,
+ ImmutableList.<ExpectedInvoiceItemCheck>of(new ExpectedInvoiceItemCheck(new LocalDate(2012, 5, 1), new LocalDate(2012, 6, 1), InvoiceItemType.RECURRING, new BigDecimal("29.95")),
+ new ExpectedInvoiceItemCheck(new LocalDate(2012, 4, 1), null, InvoiceItemType.TAX, new BigDecimal("1.0")),
+ new ExpectedInvoiceItemCheck(new LocalDate(2012, 4, 1), new LocalDate(2012, 4, 1), InvoiceItemType.CBA_ADJ, new BigDecimal("-30.95"))));
+
+ // Make sure TestInvoicePluginApi will return an additional TAX item
+ testInvoicePluginApi.addTaxItem();
+
+ // Move to Evergreen PHASE to verify non-dry-run scenario
+ busHandler.pushExpectedEvents(NextEvent.PHASE, NextEvent.INVOICE);
+ clock.addDays(30);
+ assertListenerStatus();
+
+ invoiceChecker.checkInvoice(account.getId(), 3, callContext,
+ new ExpectedInvoiceItemCheck(new LocalDate(2012, 5, 1), new LocalDate(2012, 6, 1), InvoiceItemType.RECURRING, new BigDecimal("29.95")),
+ new ExpectedInvoiceItemCheck(new LocalDate(2012, 5, 1), null, InvoiceItemType.TAX, new BigDecimal("1.0")),
+ new ExpectedInvoiceItemCheck(new LocalDate(2012, 5, 1), new LocalDate(2012, 5, 1), InvoiceItemType.CBA_ADJ, new BigDecimal("-30.95")));
+ }
+
public class TestInvoicePluginApi implements InvoicePluginApi {
AtomicBoolean addTaxItem;
diff --git a/catalog/src/main/java/org/killbill/billing/catalog/CatalogUpdater.java b/catalog/src/main/java/org/killbill/billing/catalog/CatalogUpdater.java
index 4ee0f3a..154949e 100644
--- a/catalog/src/main/java/org/killbill/billing/catalog/CatalogUpdater.java
+++ b/catalog/src/main/java/org/killbill/billing/catalog/CatalogUpdater.java
@@ -115,6 +115,8 @@ public class CatalogUpdater {
throw new CatalogApiException(ErrorCode.CAT_INVALID_SIMPLE_PLAN_DESCRIPTOR, desc);
}
+ validateNewPlanDescriptor(desc);
+
DefaultProduct product = plan != null ? (DefaultProduct) plan.getProduct() : (DefaultProduct) getExistingProduct(desc.getProductName());
if (product == null) {
product = new DefaultProduct();
@@ -126,8 +128,6 @@ public class CatalogUpdater {
if (plan == null) {
- validateNewPlanDescriptor(desc);
-
plan = new DefaultPlan();
plan.setName(desc.getPlanId());
plan.setPriceListName(PriceListSet.DEFAULT_PRICELIST_NAME);
diff --git a/entitlement/src/main/java/org/killbill/billing/entitlement/engine/core/EventsStreamBuilder.java b/entitlement/src/main/java/org/killbill/billing/entitlement/engine/core/EventsStreamBuilder.java
index 2fe8c9c..35ce153 100644
--- a/entitlement/src/main/java/org/killbill/billing/entitlement/engine/core/EventsStreamBuilder.java
+++ b/entitlement/src/main/java/org/killbill/billing/entitlement/engine/core/EventsStreamBuilder.java
@@ -66,7 +66,7 @@ import org.killbill.clock.Clock;
import org.killbill.notificationq.api.NotificationQueueService;
import org.skife.jdbi.v2.IDBI;
-import com.google.common.base.Objects;
+import com.google.common.base.MoreObjects;
import com.google.common.base.Predicate;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
@@ -184,14 +184,14 @@ public class EventsStreamBuilder {
final SubscriptionBaseBundle bundle = bundlesPerId.get(bundleId);
final List<SubscriptionBase> allSubscriptionsForBundle = subscriptions.get(bundleId);
final SubscriptionBase baseSubscription = findBaseSubscription(allSubscriptionsForBundle);
- final List<BlockingState> bundleBlockingStates = Objects.firstNonNull(blockingStatesPerBundle.get(bundleId), ImmutableList.<BlockingState>of());
+ final List<BlockingState> bundleBlockingStates = MoreObjects.firstNonNull(blockingStatesPerBundle.get(bundleId), ImmutableList.<BlockingState>of());
if (entitlementsPerBundle.get(bundleId) == null) {
entitlementsPerBundle.put(bundleId, new LinkedList<EventsStream>());
}
for (final SubscriptionBase subscription : allSubscriptionsForBundle) {
- final List<BlockingState> subscriptionBlockingStatesOnDisk = Objects.firstNonNull(blockingStatesPerSubscription.get(subscription.getId()), ImmutableList.<BlockingState>of());
+ final List<BlockingState> subscriptionBlockingStatesOnDisk = MoreObjects.firstNonNull(blockingStatesPerSubscription.get(subscription.getId()), ImmutableList.<BlockingState>of());
// We cannot always use blockingStatesForAccount here: we need subscriptionBlockingStates to contain the events not on disk when building an EventsStream
// for an add-on - which means going through the magic of ProxyBlockingStateDao, which will recursively
@@ -294,8 +294,8 @@ public class EventsStreamBuilder {
}
}
- final List<BlockingState> bundleBlockingStates = Objects.firstNonNull(blockingStatesPerBundle.get(subscription.getBundleId()), ImmutableList.<BlockingState>of());
- final List<BlockingState> subscriptionBlockingStatesOnDisk = Objects.firstNonNull(blockingStatesPerSubscription.get(subscription.getId()), ImmutableList.<BlockingState>of());
+ final List<BlockingState> bundleBlockingStates = MoreObjects.firstNonNull(blockingStatesPerBundle.get(subscription.getBundleId()), ImmutableList.<BlockingState>of());
+ final List<BlockingState> subscriptionBlockingStatesOnDisk = MoreObjects.firstNonNull(blockingStatesPerSubscription.get(subscription.getId()), ImmutableList.<BlockingState>of());
// We cannot always use blockingStatesForAccount here: we need subscriptionBlockingStates to contain the events not on disk when building an EventsStream
// for an add-on - which means going through the magic of ProxyBlockingStateDao, which will recursively
diff --git a/entitlement/src/test/java/org/killbill/billing/entitlement/dao/MockBlockingStateDao.java b/entitlement/src/test/java/org/killbill/billing/entitlement/dao/MockBlockingStateDao.java
index bc51a98..21539e3 100644
--- a/entitlement/src/test/java/org/killbill/billing/entitlement/dao/MockBlockingStateDao.java
+++ b/entitlement/src/test/java/org/killbill/billing/entitlement/dao/MockBlockingStateDao.java
@@ -34,7 +34,7 @@ import org.killbill.billing.entitlement.api.BlockingStateType;
import org.killbill.billing.entitlement.api.EntitlementApiException;
import org.killbill.billing.util.entity.dao.MockEntityDaoBase;
-import com.google.common.base.Objects;
+import com.google.common.base.MoreObjects;
import com.google.common.base.Optional;
import com.google.common.base.Predicate;
import com.google.common.collect.Collections2;
@@ -81,7 +81,7 @@ public class MockBlockingStateDao extends MockEntityDaoBase<BlockingStateModelDa
@Override
public List<BlockingState> getBlockingAllForAccountRecordId(final InternalTenantContext context) {
- return Objects.firstNonNull(blockingStatesPerAccountRecordId.get(context.getAccountRecordId()), ImmutableList.<BlockingState>of());
+ return MoreObjects.firstNonNull(blockingStatesPerAccountRecordId.get(context.getAccountRecordId()), ImmutableList.<BlockingState>of());
}
@Override
diff --git a/entitlement/src/test/java/org/killbill/billing/entitlement/engine/core/TestEntitlementUtils.java b/entitlement/src/test/java/org/killbill/billing/entitlement/engine/core/TestEntitlementUtils.java
index 2f98acc..1a0ced3 100644
--- a/entitlement/src/test/java/org/killbill/billing/entitlement/engine/core/TestEntitlementUtils.java
+++ b/entitlement/src/test/java/org/killbill/billing/entitlement/engine/core/TestEntitlementUtils.java
@@ -33,7 +33,6 @@ import org.killbill.billing.catalog.api.BillingPeriod;
import org.killbill.billing.catalog.api.PlanPhaseSpecifier;
import org.killbill.billing.catalog.api.PlanSpecifier;
import org.killbill.billing.catalog.api.PriceListSet;
-import org.killbill.billing.catalog.api.ProductCategory;
import org.killbill.billing.entitlement.AccountEventsStreams;
import org.killbill.billing.entitlement.EntitlementService;
import org.killbill.billing.entitlement.EntitlementTestSuiteWithEmbeddedDB;
@@ -50,7 +49,7 @@ import org.testng.Assert;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
-import com.google.common.base.Objects;
+import com.google.common.base.MoreObjects;
import com.google.common.base.Predicate;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
@@ -395,10 +394,10 @@ public class TestEntitlementUtils extends EntitlementTestSuiteWithEmbeddedDB {
// Test the "write" path
private void checkActualBlockingStatesToCancel(final DefaultEntitlement baseEntitlement, final DefaultEntitlement addOnEntitlement, @Nullable final DateTime effectiveCancellationDateTime, final boolean approximateDateCheck) throws EntitlementApiException {
- final Collection<BlockingState> blockingStatesForCancellationViaEntitlement = computeBlockingStatesForAssociatedAddonsViaEntitlement(baseEntitlement, Objects.firstNonNull(effectiveCancellationDateTime, initialDate.toDateTimeAtStartOfDay()));
+ final Collection<BlockingState> blockingStatesForCancellationViaEntitlement = computeBlockingStatesForAssociatedAddonsViaEntitlement(baseEntitlement, MoreObjects.firstNonNull(effectiveCancellationDateTime, initialDate.toDateTimeAtStartOfDay()));
doCheckActualBlockingStatesToCancel(addOnEntitlement, effectiveCancellationDateTime, approximateDateCheck, blockingStatesForCancellationViaEntitlement);
- final Collection<BlockingState> blockingStatesForCancellationViaAccount = computeBlockingStatesForAssociatedAddonsViaAccount(baseEntitlement, Objects.firstNonNull(effectiveCancellationDateTime, initialDate.toDateTimeAtStartOfDay()));
+ final Collection<BlockingState> blockingStatesForCancellationViaAccount = computeBlockingStatesForAssociatedAddonsViaAccount(baseEntitlement, MoreObjects.firstNonNull(effectiveCancellationDateTime, initialDate.toDateTimeAtStartOfDay()));
doCheckActualBlockingStatesToCancel(addOnEntitlement, effectiveCancellationDateTime, approximateDateCheck, blockingStatesForCancellationViaAccount);
}
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/api/InvoiceApiHelper.java b/invoice/src/main/java/org/killbill/billing/invoice/api/InvoiceApiHelper.java
index 7e8c68c..cb7dfcb 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/api/InvoiceApiHelper.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/api/InvoiceApiHelper.java
@@ -50,7 +50,7 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.common.base.Function;
-import com.google.common.base.Objects;
+import com.google.common.base.MoreObjects;
import com.google.common.base.Predicate;
import com.google.common.collect.Collections2;
import com.google.common.collect.ImmutableList;
@@ -140,7 +140,7 @@ public class InvoiceApiHelper {
}
// Check the specified currency matches the one of the existing invoice
- final Currency currencyForAdjustment = Objects.firstNonNull(currency, invoiceItemToBeAdjusted.getCurrency());
+ final Currency currencyForAdjustment = MoreObjects.firstNonNull(currency, invoiceItemToBeAdjusted.getCurrency());
if (invoiceItemToBeAdjusted.getCurrency() != currencyForAdjustment) {
throw new InvoiceApiException(ErrorCode.CURRENCY_INVALID, currency, invoiceItemToBeAdjusted.getCurrency());
}
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/api/user/DefaultInvoiceUserApi.java b/invoice/src/main/java/org/killbill/billing/invoice/api/user/DefaultInvoiceUserApi.java
index 3cad92b..c19bb7c 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/api/user/DefaultInvoiceUserApi.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/api/user/DefaultInvoiceUserApi.java
@@ -499,8 +499,9 @@ public class DefaultInvoiceUserApi implements InvoiceUserApi {
}
}));
+ migrationInvoice.addInvoiceItems(itemModelDaos);
- dao.createInvoice(migrationInvoice, itemModelDaos, true, new FutureAccountNotifications(ImmutableMap.<UUID, List<SubscriptionNotification>>of()), internalCallContext);
+ dao.createInvoices(ImmutableList.<InvoiceModelDao>of(migrationInvoice), internalCallContext);
return migrationInvoice.getId();
}
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/dao/CBADao.java b/invoice/src/main/java/org/killbill/billing/invoice/dao/CBADao.java
index 230fdd1..48de156 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/dao/CBADao.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/dao/CBADao.java
@@ -1,7 +1,9 @@
/*
* Copyright 2010-2013 Ning, Inc.
+ * Copyright 2014-2017 Groupon, Inc
+ * Copyright 2014-2017 The Billing Project, LLC
*
- * Ning licenses this file to you under the Apache License, version 2.0
+ * The Billing Project licenses this file to you under the Apache License, version 2.0
* (the "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at:
*
@@ -24,14 +26,14 @@ import java.util.UUID;
import javax.annotation.Nullable;
import javax.inject.Inject;
-import org.killbill.billing.invoice.api.InvoiceApiException;
-import org.killbill.billing.invoice.api.InvoiceItem;
-import org.killbill.billing.invoice.api.InvoiceItemType;
-import org.killbill.billing.invoice.model.CreditBalanceAdjInvoiceItem;
import org.killbill.billing.callcontext.InternalCallContext;
import org.killbill.billing.callcontext.InternalTenantContext;
import org.killbill.billing.entity.EntityPersistenceException;
+import org.killbill.billing.invoice.api.InvoiceApiException;
+import org.killbill.billing.invoice.api.InvoiceStatus;
+import org.killbill.billing.invoice.model.CreditBalanceAdjInvoiceItem;
import org.killbill.billing.util.entity.dao.EntitySqlDaoWrapperFactory;
+import org.killbill.billing.util.tag.Tag;
import com.google.common.base.Predicate;
import com.google.common.collect.Iterables;
@@ -46,41 +48,35 @@ public class CBADao {
this.invoiceDaoHelper = invoiceDaoHelper;
}
-
- public BigDecimal getAccountCBAFromTransaction(final UUID accountId,
- final EntitySqlDaoWrapperFactory entitySqlDaoWrapperFactory,
- final InternalTenantContext context) {
- final List<InvoiceModelDao> invoices = invoiceDaoHelper.getAllInvoicesByAccountFromTransaction(entitySqlDaoWrapperFactory, context);
- return getAccountCBAFromTransaction(invoices);
- }
-
- public BigDecimal getAccountCBAFromTransaction(final List<InvoiceModelDao> invoices) {
- BigDecimal cba = BigDecimal.ZERO;
- for (final InvoiceModelDao cur : invoices) {
- cba = cba.add(InvoiceModelDaoHelper.getCBAAmount(cur));
- }
- return cba;
+ // PERF: Compute the CBA directly in the database (faster than re-constructing all invoices)
+ public BigDecimal getAccountCBAFromTransaction(final EntitySqlDaoWrapperFactory entitySqlDaoWrapperFactory, final InternalTenantContext context) {
+ final InvoiceItemSqlDao invoiceItemSqlDao = entitySqlDaoWrapperFactory.become(InvoiceItemSqlDao.class);
+ return invoiceItemSqlDao.getAccountCBA(context);
}
// We expect a clean up to date invoice, with all the items except the cba, that we will compute in that method
- public InvoiceItemModelDao computeCBAComplexity(final InvoiceModelDao invoice, final EntitySqlDaoWrapperFactory entitySqlDaoWrapperFactory, final InternalCallContext context) throws EntityPersistenceException, InvoiceApiException {
-
+ public InvoiceItemModelDao computeCBAComplexity(final InvoiceModelDao invoice,
+ @Nullable final BigDecimal accountCBAOrNull,
+ @Nullable final EntitySqlDaoWrapperFactory entitySqlDaoWrapperFactory,
+ final InternalCallContext context) throws EntityPersistenceException, InvoiceApiException {
final BigDecimal balance = getInvoiceBalance(invoice);
- // Current balance is negative, we need to generate a credit (positive CBA amount).
if (balance.compareTo(BigDecimal.ZERO) < 0) {
- return new InvoiceItemModelDao(new CreditBalanceAdjInvoiceItem(invoice.getId(), invoice.getAccountId(), context.getCreatedDate().toLocalDate(), balance.negate(), invoice.getCurrency()));
-
- // Current balance is positive, we need to use some of the existing if available (negative CBA amount)
- } else if (balance.compareTo(BigDecimal.ZERO) > 0) {
+ // Current balance is negative, we need to generate a credit (positive CBA amount)
+ return buildCBAItem(invoice, balance, context);
+ } else if (balance.compareTo(BigDecimal.ZERO) > 0 && invoice.getStatus() == InvoiceStatus.COMMITTED) {
+ // Current balance is positive and the invoice is COMMITTED, we need to use some of the existing if available (negative CBA amount)
+ // PERF: in some codepaths, the CBA maybe have already been computed
+ BigDecimal accountCBA = accountCBAOrNull;
+ if (accountCBAOrNull == null) {
+ accountCBA = getAccountCBAFromTransaction(entitySqlDaoWrapperFactory, context);
+ }
- final List<InvoiceModelDao> allInvoices = invoiceDaoHelper.getAllInvoicesByAccountFromTransaction(entitySqlDaoWrapperFactory, context);
- final BigDecimal accountCBA = getAccountCBAFromTransaction(allInvoices);
if (accountCBA.compareTo(BigDecimal.ZERO) <= 0) {
return null;
}
final BigDecimal positiveCreditAmount = accountCBA.compareTo(balance) > 0 ? balance : accountCBA;
- return new InvoiceItemModelDao(new CreditBalanceAdjInvoiceItem(invoice.getId(), invoice.getAccountId(), context.getCreatedDate().toLocalDate(), positiveCreditAmount.negate(), invoice.getCurrency()));
+ return buildCBAItem(invoice, positiveCreditAmount, context);
} else {
// 0 balance, nothing to do.
return null;
@@ -113,64 +109,53 @@ public class CBADao {
}
// We let the code below rehydrate the invoice before we can add the CBA item
- public void addCBAComplexityFromTransaction(final UUID invoiceId, final EntitySqlDaoWrapperFactory entitySqlDaoWrapperFactory, final InternalCallContext context) throws EntityPersistenceException, InvoiceApiException {
-
+ // PERF: when possible, prefer the method below to avoid re-fetching the invoice
+ public void doCBAComplexityFromTransaction(final UUID invoiceId,
+ final List<Tag> invoicesTags,
+ final EntitySqlDaoWrapperFactory entitySqlDaoWrapperFactory,
+ final InternalCallContext context) throws EntityPersistenceException, InvoiceApiException {
final InvoiceSqlDao transInvoiceDao = entitySqlDaoWrapperFactory.become(InvoiceSqlDao.class);
final InvoiceModelDao invoice = transInvoiceDao.getById(invoiceId.toString(), context);
- invoiceDaoHelper.populateChildren(invoice, entitySqlDaoWrapperFactory, context);
- addCBAComplexityFromTransaction(invoice, entitySqlDaoWrapperFactory, context);
- }
+ invoiceDaoHelper.populateChildren(invoice, invoicesTags, entitySqlDaoWrapperFactory, context);
- // We expect a clean up to date invoice, with all the items except the CBA, that we will compute in that method
- public void addCBAComplexityFromTransaction(final InvoiceModelDao invoice, final EntitySqlDaoWrapperFactory entitySqlDaoWrapperFactory, final InternalCallContext context) throws EntityPersistenceException, InvoiceApiException {
- final InvoiceItemModelDao cbaItem = computeCBAComplexity(invoice, entitySqlDaoWrapperFactory, context);
- if (cbaItem != null) {
- final InvoiceItemSqlDao transInvoiceItemDao = entitySqlDaoWrapperFactory.become(InvoiceItemSqlDao.class);
- transInvoiceItemDao.create(cbaItem, context);
- }
- List<InvoiceModelDao> invoiceItemModelDaos = invoiceDaoHelper.getAllInvoicesByAccountFromTransaction(entitySqlDaoWrapperFactory, context);
- useExistingCBAFromTransaction(invoiceItemModelDaos, entitySqlDaoWrapperFactory, context);
+ doCBAComplexityFromTransaction(invoice, invoicesTags, entitySqlDaoWrapperFactory, context);
}
- public void addCBAComplexityFromTransaction(final EntitySqlDaoWrapperFactory entitySqlDaoWrapperFactory, final InternalCallContext context) throws EntityPersistenceException, InvoiceApiException {
-
- List<InvoiceModelDao> invoiceItemModelDaos = invoiceDaoHelper.getAllInvoicesByAccountFromTransaction(entitySqlDaoWrapperFactory, context);
- for (InvoiceModelDao cur : invoiceItemModelDaos) {
- addCBAIfNeeded(entitySqlDaoWrapperFactory, cur, context);
- }
- invoiceItemModelDaos = invoiceDaoHelper.getAllInvoicesByAccountFromTransaction(entitySqlDaoWrapperFactory, context);
- useExistingCBAFromTransaction(invoiceItemModelDaos, entitySqlDaoWrapperFactory, context);
+ public void doCBAComplexityFromTransaction(final List<Tag> invoicesTags,
+ final EntitySqlDaoWrapperFactory entitySqlDaoWrapperFactory,
+ final InternalCallContext context) throws EntityPersistenceException, InvoiceApiException {
+ doCBAComplexityFromTransaction((InvoiceModelDao) null, invoicesTags, entitySqlDaoWrapperFactory, context);
}
- /**
- * Adjust the invoice with a CBA item if the new invoice balance is negative.
- *
- * @param entitySqlDaoWrapperFactory the EntitySqlDaoWrapperFactory from the current transaction
- * @param invoice the invoice to adjust
- * @param context the call callcontext
- */
- private void addCBAIfNeeded(final EntitySqlDaoWrapperFactory entitySqlDaoWrapperFactory,
- final InvoiceModelDao invoice,
- final InternalCallContext context) throws EntityPersistenceException {
-
- // If invoice balance becomes negative we add some CBA item
- final BigDecimal balance = InvoiceModelDaoHelper.getBalance(invoice);
- if (balance.compareTo(BigDecimal.ZERO) < 0) {
- final InvoiceItemSqlDao transInvoiceItemDao = entitySqlDaoWrapperFactory.become(InvoiceItemSqlDao.class);
- final InvoiceItemModelDao cbaAdjItem = new InvoiceItemModelDao(new CreditBalanceAdjInvoiceItem(invoice.getId(), invoice.getAccountId(), context.getCreatedDate().toLocalDate(), balance.negate(), invoice.getCurrency()));
- transInvoiceItemDao.create(cbaAdjItem, context);
+ // Note! We expect an *up-to-date* invoice, with all the items and payments except the CBA, that we will compute in that method
+ public void doCBAComplexityFromTransaction(@Nullable final InvoiceModelDao invoice,
+ final List<Tag> invoicesTags,
+ final EntitySqlDaoWrapperFactory entitySqlDaoWrapperFactory,
+ final InternalCallContext context) throws EntityPersistenceException, InvoiceApiException {
+ // PERF: It is expensive to retrieve and construct all invoice objects. To check if there is effectively something to use, compute the CBA by the database first
+ BigDecimal remainingAccountCBA = getAccountCBAFromTransaction(entitySqlDaoWrapperFactory, context);
+
+ if (invoice != null) {
+ // Generate or use CBA for that specific invoice
+ remainingAccountCBA = computeCBAComplexityAndCreateCBAItem(remainingAccountCBA, invoice, entitySqlDaoWrapperFactory, context);
}
- }
+ useExistingCBAFromTransaction(remainingAccountCBA, invoicesTags, entitySqlDaoWrapperFactory, context);
+ }
- private void useExistingCBAFromTransaction(final List<InvoiceModelDao> invoices, final EntitySqlDaoWrapperFactory entitySqlDaoWrapperFactory, final InternalCallContext context) throws InvoiceApiException, EntityPersistenceException {
-
- final BigDecimal accountCBA = getAccountCBAFromTransaction(invoices);
+ // Distribute account CBA across all COMMITTED unpaid invoices
+ private void useExistingCBAFromTransaction(final BigDecimal accountCBA,
+ final List<Tag> invoicesTags,
+ final EntitySqlDaoWrapperFactory entitySqlDaoWrapperFactory,
+ final InternalCallContext context) throws InvoiceApiException, EntityPersistenceException {
if (accountCBA.compareTo(BigDecimal.ZERO) <= 0) {
return;
}
- final List<InvoiceModelDao> unpaidInvoices = invoiceDaoHelper.getUnpaidInvoicesByAccountFromTransaction(invoices, null);
+ // PERF: Computing the invoice balance is difficult to do in the DB, so we effectively need to retrieve all invoices on the account and filter the unpaid ones in memory.
+ // This should be infrequent though because of the account CBA check above.
+ final List<InvoiceModelDao> allInvoices = invoiceDaoHelper.getAllInvoicesByAccountFromTransaction(invoicesTags, entitySqlDaoWrapperFactory, context);
+ final List<InvoiceModelDao> unpaidInvoices = invoiceDaoHelper.getUnpaidInvoicesByAccountFromTransaction(allInvoices, null);
// We order the same os BillingStateCalculator-- should really share the comparator
final List<InvoiceModelDao> orderedUnpaidInvoices = Ordering.from(new Comparator<InvoiceModelDao>() {
@Override
@@ -180,20 +165,46 @@ public class CBADao {
}).immutableSortedCopy(unpaidInvoices);
BigDecimal remainingAccountCBA = accountCBA;
- for (InvoiceModelDao cur : orderedUnpaidInvoices) {
- final BigDecimal curInvoiceBalance = InvoiceModelDaoHelper.getBalance(cur);
- final BigDecimal cbaToApplyOnInvoice = remainingAccountCBA.compareTo(curInvoiceBalance) <= 0 ? remainingAccountCBA : curInvoiceBalance;
- remainingAccountCBA = remainingAccountCBA.subtract(cbaToApplyOnInvoice);
-
- final InvoiceItemModelDao cbaAdjItem = new InvoiceItemModelDao(new CreditBalanceAdjInvoiceItem(cur.getId(), cur.getAccountId(), context.getCreatedDate().toLocalDate(), cbaToApplyOnInvoice.negate(), cur.getCurrency()));
-
- final InvoiceItemSqlDao transInvoiceItemDao = entitySqlDaoWrapperFactory.become(InvoiceItemSqlDao.class);
- transInvoiceItemDao.create(cbaAdjItem, context);
-
+ for (final InvoiceModelDao unpaidInvoice : orderedUnpaidInvoices) {
+ remainingAccountCBA = computeCBAComplexityAndCreateCBAItem(remainingAccountCBA, unpaidInvoice, entitySqlDaoWrapperFactory, context);
if (remainingAccountCBA.compareTo(BigDecimal.ZERO) <= 0) {
break;
}
}
}
+ // Return the updated account CBA
+ private BigDecimal computeCBAComplexityAndCreateCBAItem(final BigDecimal accountCBA,
+ final InvoiceModelDao invoice,
+ final EntitySqlDaoWrapperFactory entitySqlDaoWrapperFactory,
+ final InternalCallContext context) throws EntityPersistenceException, InvoiceApiException {
+ final InvoiceItemModelDao cbaItem = computeCBAComplexity(invoice, accountCBA, entitySqlDaoWrapperFactory, context);
+ if (cbaItem != null) {
+ createCBAItem(invoice, cbaItem, entitySqlDaoWrapperFactory, context);
+ return accountCBA.add(cbaItem.getAmount());
+ } else {
+ return accountCBA;
+ }
+ }
+
+ private void createCBAItem(final InvoiceModelDao invoiceModelDao,
+ final InvoiceItemModelDao cbaItem,
+ final EntitySqlDaoWrapperFactory entitySqlDaoWrapperFactory,
+ final InternalCallContext context) throws EntityPersistenceException {
+ final InvoiceItemSqlDao transInvoiceItemDao = entitySqlDaoWrapperFactory.become(InvoiceItemSqlDao.class);
+ transInvoiceItemDao.create(cbaItem, context);
+
+ // Refresh the in-memory item
+ invoiceModelDao.addInvoiceItem(cbaItem);
+ }
+
+ private InvoiceItemModelDao buildCBAItem(final InvoiceModelDao invoice,
+ final BigDecimal amount,
+ final InternalCallContext context) {
+ return new InvoiceItemModelDao(new CreditBalanceAdjInvoiceItem(invoice.getId(),
+ invoice.getAccountId(),
+ context.getCreatedDate().toLocalDate(),
+ amount.negate(),
+ invoice.getCurrency()));
+ }
}
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 4d64d57..4976f49 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
@@ -1,7 +1,7 @@
/*
* Copyright 2010-2013 Ning, Inc.
- * Copyright 2014-2016 Groupon, Inc
- * Copyright 2014-2016 The Billing Project, LLC
+ * Copyright 2014-2017 Groupon, Inc
+ * Copyright 2014-2017 The Billing Project, LLC
*
* The Billing Project licenses this file to you under the Apache License, version 2.0
* (the "License"); you may not use this file except in compliance with the
@@ -20,6 +20,8 @@ package org.killbill.billing.invoice.dao;
import java.math.BigDecimal;
import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
@@ -54,6 +56,7 @@ import org.killbill.billing.invoice.model.DefaultInvoice;
import org.killbill.billing.invoice.model.ExternalChargeInvoiceItem;
import org.killbill.billing.invoice.notification.NextBillingDatePoster;
import org.killbill.billing.invoice.notification.ParentInvoiceCommitmentPoster;
+import org.killbill.billing.tag.TagInternalApi;
import org.killbill.billing.util.UUIDs;
import org.killbill.billing.util.cache.Cachable.CacheType;
import org.killbill.billing.util.cache.CacheControllerDispatcher;
@@ -67,6 +70,7 @@ import org.killbill.billing.util.entity.dao.EntityDaoBase;
import org.killbill.billing.util.entity.dao.EntitySqlDaoTransactionWrapper;
import org.killbill.billing.util.entity.dao.EntitySqlDaoTransactionalJdbiWrapper;
import org.killbill.billing.util.entity.dao.EntitySqlDaoWrapperFactory;
+import org.killbill.billing.util.tag.Tag;
import org.killbill.bus.api.BusEvent;
import org.killbill.bus.api.PersistentBus;
import org.killbill.bus.api.PersistentBus.EventBusException;
@@ -79,8 +83,9 @@ import com.google.common.base.Function;
import com.google.common.base.Preconditions;
import com.google.common.base.Predicate;
import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
-import com.google.common.collect.Iterators;
import com.google.common.collect.Ordering;
import com.google.inject.Inject;
@@ -113,9 +118,11 @@ public class DefaultInvoiceDao extends EntityDaoBase<InvoiceModelDao, Invoice, I
private final CacheControllerDispatcher cacheControllerDispatcher;
private final NonEntityDao nonEntityDao;
private final ParentInvoiceCommitmentPoster parentInvoiceCommitmentPoster;
+ private final TagInternalApi tagInternalApi;
@Inject
- public DefaultInvoiceDao(final IDBI dbi,
+ public DefaultInvoiceDao(final TagInternalApi tagInternalApi,
+ final IDBI dbi,
final NextBillingDatePoster nextBillingDatePoster,
final PersistentBus eventBus,
final Clock clock,
@@ -127,6 +134,7 @@ public class DefaultInvoiceDao extends EntityDaoBase<InvoiceModelDao, Invoice, I
final ParentInvoiceCommitmentPoster parentInvoiceCommitmentPoster,
final InternalCallContextFactory internalCallContextFactory) {
super(new EntitySqlDaoTransactionalJdbiWrapper(dbi, clock, cacheControllerDispatcher, nonEntityDao, internalCallContextFactory), InvoiceSqlDao.class);
+ this.tagInternalApi = tagInternalApi;
this.nextBillingDatePoster = nextBillingDatePoster;
this.eventBus = eventBus;
this.invoiceConfig = invoiceConfig;
@@ -146,6 +154,8 @@ public class DefaultInvoiceDao extends EntityDaoBase<InvoiceModelDao, Invoice, I
@Override
public List<InvoiceModelDao> getInvoicesByAccount(final InternalTenantContext context) {
+ final List<Tag> invoicesTags = getInvoicesTags(context);
+
return transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<List<InvoiceModelDao>>() {
@Override
public List<InvoiceModelDao> inTransaction(final EntitySqlDaoWrapperFactory entitySqlDaoWrapperFactory) throws Exception {
@@ -158,7 +168,7 @@ public class DefaultInvoiceDao extends EntityDaoBase<InvoiceModelDao, Invoice, I
return !invoice.isMigrated();
}
})));
- invoiceDaoHelper.populateChildren(invoices, entitySqlDaoWrapperFactory, context);
+ invoiceDaoHelper.populateChildren(invoices, invoicesTags, entitySqlDaoWrapperFactory, context);
return invoices;
}
@@ -167,22 +177,26 @@ public class DefaultInvoiceDao extends EntityDaoBase<InvoiceModelDao, Invoice, I
@Override
public List<InvoiceModelDao> getAllInvoicesByAccount(final InternalTenantContext context) {
+ final List<Tag> invoicesTags = getInvoicesTags(context);
+
return transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<List<InvoiceModelDao>>() {
@Override
public List<InvoiceModelDao> inTransaction(final EntitySqlDaoWrapperFactory entitySqlDaoWrapperFactory) throws Exception {
- return invoiceDaoHelper.getAllInvoicesByAccountFromTransaction(entitySqlDaoWrapperFactory, context);
+ return invoiceDaoHelper.getAllInvoicesByAccountFromTransaction(invoicesTags, entitySqlDaoWrapperFactory, context);
}
});
}
@Override
public List<InvoiceModelDao> getInvoicesByAccount(final LocalDate fromDate, final InternalTenantContext context) {
+ final List<Tag> invoicesTags = getInvoicesTags(context);
+
return transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<List<InvoiceModelDao>>() {
@Override
public List<InvoiceModelDao> inTransaction(final EntitySqlDaoWrapperFactory entitySqlDaoWrapperFactory) throws Exception {
final InvoiceSqlDao invoiceDao = entitySqlDaoWrapperFactory.become(InvoiceSqlDao.class);
final List<InvoiceModelDao> invoices = getAllNonMigratedInvoicesByAccountAfterDate(invoiceDao, fromDate, context);
- invoiceDaoHelper.populateChildren(invoices, entitySqlDaoWrapperFactory, context);
+ invoiceDaoHelper.populateChildren(invoices, invoicesTags, entitySqlDaoWrapperFactory, context);
return invoices;
}
@@ -201,6 +215,8 @@ public class DefaultInvoiceDao extends EntityDaoBase<InvoiceModelDao, Invoice, I
@Override
public InvoiceModelDao getById(final UUID invoiceId, final InternalTenantContext context) {
+ final List<Tag> invoicesTags = getInvoicesTags(context);
+
return transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<InvoiceModelDao>() {
@Override
public InvoiceModelDao inTransaction(final EntitySqlDaoWrapperFactory entitySqlDaoWrapperFactory) throws Exception {
@@ -210,7 +226,7 @@ public class DefaultInvoiceDao extends EntityDaoBase<InvoiceModelDao, Invoice, I
if (invoice == null) {
throw new InvoiceApiException(ErrorCode.INVOICE_NOT_FOUND, invoiceId);
}
- invoiceDaoHelper.populateChildren(invoice, entitySqlDaoWrapperFactory, context);
+ invoiceDaoHelper.populateChildren(invoice, invoicesTags, entitySqlDaoWrapperFactory, context);
return invoice;
}
});
@@ -222,6 +238,8 @@ public class DefaultInvoiceDao extends EntityDaoBase<InvoiceModelDao, Invoice, I
throw new InvoiceApiException(ErrorCode.INVOICE_INVALID_NUMBER, "(null)");
}
+ final List<Tag> invoicesTags = getInvoicesTags(context);
+
return transactionalSqlDao.execute(InvoiceApiException.class, new EntitySqlDaoTransactionWrapper<InvoiceModelDao>() {
@Override
public InvoiceModelDao inTransaction(final EntitySqlDaoWrapperFactory entitySqlDaoWrapperFactory) throws Exception {
@@ -235,7 +253,7 @@ public class DefaultInvoiceDao extends EntityDaoBase<InvoiceModelDao, Invoice, I
// The context may not contain the account record id at this point - we couldn't do it in the API above
// as we couldn't get access to the invoice object until now.
final InternalTenantContext contextWithAccountRecordId = internalCallContextFactory.createInternalTenantContext(invoice.getAccountId(), context);
- invoiceDaoHelper.populateChildren(invoice, entitySqlDaoWrapperFactory, contextWithAccountRecordId);
+ invoiceDaoHelper.populateChildren(invoice, invoicesTags, entitySqlDaoWrapperFactory, contextWithAccountRecordId);
return invoice;
}
@@ -257,42 +275,40 @@ public class DefaultInvoiceDao extends EntityDaoBase<InvoiceModelDao, Invoice, I
}
@Override
- public void createInvoice(final InvoiceModelDao invoice, final List<InvoiceItemModelDao> invoiceItems,
- final boolean isRealInvoice, final FutureAccountNotifications callbackDateTimePerSubscriptions,
+ public void createInvoice(final InvoiceModelDao invoice,
+ final FutureAccountNotifications callbackDateTimePerSubscriptions,
final InternalCallContext context) {
- transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<Void>() {
- @Override
- public Void inTransaction(final EntitySqlDaoWrapperFactory entitySqlDaoWrapperFactory) throws Exception {
- final InvoiceSqlDao transactional = entitySqlDaoWrapperFactory.become(InvoiceSqlDao.class);
+ createInvoices(ImmutableList.<InvoiceModelDao>of(invoice), callbackDateTimePerSubscriptions, context);
+ }
- final InvoiceModelDao currentInvoice = transactional.getById(invoice.getId().toString(), context);
- if (currentInvoice == null) {
- // We only want to insert that invoice if there are real invoiceItems associated to it -- if not, this is just
- // a shell invoice and we only need to insert the invoiceItems -- for the already existing invoices
- if (isRealInvoice) {
- transactional.create(invoice, context);
- }
+ @Override
+ public List<InvoiceItemModelDao> createInvoices(final List<InvoiceModelDao> invoices,
+ final InternalCallContext context) {
+ return createInvoices(invoices, new FutureAccountNotifications(ImmutableMap.<UUID, List<SubscriptionNotification>>of()), context);
+ }
- // Create the invoice items
- final InvoiceItemSqlDao transInvoiceItemSqlDao = entitySqlDaoWrapperFactory.become(InvoiceItemSqlDao.class);
- for (final InvoiceItemModelDao invoiceItemModelDao : invoiceItems) {
- createInvoiceItemFromTransaction(transInvoiceItemSqlDao, invoiceItemModelDao, context);
- }
- cbaDao.addCBAComplexityFromTransaction(invoice, entitySqlDaoWrapperFactory, context);
- if (InvoiceStatus.COMMITTED.equals(invoice.getStatus())) {
- notifyOfFutureBillingEvents(entitySqlDaoWrapperFactory, invoice.getAccountId(), callbackDateTimePerSubscriptions, context);
- }
- if (invoice.isParentInvoice()) {
- notifyOfParentInvoiceCreation(entitySqlDaoWrapperFactory, invoice, callbackDateTimePerSubscriptions, context);
- }
- }
- return null;
+ private List<InvoiceItemModelDao> createInvoices(final Iterable<InvoiceModelDao> invoices,
+ final FutureAccountNotifications callbackDateTimePerSubscriptions,
+ final InternalCallContext context) {
+ final Collection<UUID> createdInvoiceIds = new HashSet<UUID>();
+ final Collection<UUID> adjustedInvoiceIds = new HashSet<UUID>();
+ final Collection<UUID> committedInvoiceIds = new HashSet<UUID>();
+
+ final Collection<UUID> uniqueInvoiceIds = new HashSet<UUID>();
+ for (final InvoiceModelDao invoiceModelDao : invoices) {
+ for (final InvoiceItemModelDao invoiceItemModelDao : invoiceModelDao.getInvoiceItems()) {
+ uniqueInvoiceIds.add(invoiceItemModelDao.getInvoiceId());
}
- });
- }
+ }
- @Override
- public List<InvoiceItemModelDao> createInvoices(final List<InvoiceModelDao> invoices, final InternalCallContext context) {
+ if (Iterables.<InvoiceModelDao>isEmpty(invoices)) {
+ return ImmutableList.<InvoiceItemModelDao>of();
+ }
+ final UUID accountId = invoices.iterator().next().getAccountId();
+
+ final List<Tag> invoicesTags = getInvoicesTags(context);
+
+ final Map<UUID, InvoiceModelDao> invoiceByInvoiceId = new HashMap<UUID, InvoiceModelDao>();
return transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<List<InvoiceItemModelDao>>() {
@Override
public List<InvoiceItemModelDao> inTransaction(final EntitySqlDaoWrapperFactory entitySqlDaoWrapperFactory) throws Exception {
@@ -301,37 +317,58 @@ public class DefaultInvoiceDao extends EntityDaoBase<InvoiceModelDao, Invoice, I
final List<InvoiceItemModelDao> createdInvoiceItems = new LinkedList<InvoiceItemModelDao>();
for (final InvoiceModelDao invoiceModelDao : invoices) {
- boolean madeChanges = false;
- boolean newInvoice = false;
+ invoiceByInvoiceId.put(invoiceModelDao.getId(), invoiceModelDao);
+ final boolean isRealInvoice = uniqueInvoiceIds.remove(invoiceModelDao.getId());
// Create the invoice if needed
if (invoiceSqlDao.getById(invoiceModelDao.getId().toString(), context) == null) {
- invoiceSqlDao.create(invoiceModelDao, context);
- madeChanges = true;
- newInvoice = true;
+ // We only want to insert that invoice if there are real invoiceItems associated to it -- if not, this is just
+ // a shell invoice and we only need to insert the invoiceItems -- for the already existing invoices
+ if (isRealInvoice) {
+ invoiceSqlDao.create(invoiceModelDao, context);
+ createdInvoiceIds.add(invoiceModelDao.getId());
+ }
}
- // Create the invoice items if needed
+ // Create the invoice items if needed (note: they may not necessarily belong to that invoice)
for (final InvoiceItemModelDao invoiceItemModelDao : invoiceModelDao.getInvoiceItems()) {
if (transInvoiceItemSqlDao.getById(invoiceItemModelDao.getId().toString(), context) == null) {
createInvoiceItemFromTransaction(transInvoiceItemSqlDao, invoiceItemModelDao, context);
createdInvoiceItems.add(transInvoiceItemSqlDao.getById(invoiceItemModelDao.getId().toString(), context));
- madeChanges = true;
- }
- }
- if (madeChanges) {
- cbaDao.addCBAComplexityFromTransaction(invoiceModelDao.getId(), entitySqlDaoWrapperFactory, context);
+ adjustedInvoiceIds.add(invoiceItemModelDao.getInvoiceId());
+ }
}
+ final boolean wasInvoiceCreated = createdInvoiceIds.contains(invoiceModelDao.getId());
if (InvoiceStatus.COMMITTED.equals(invoiceModelDao.getStatus())) {
- if (newInvoice) {
+ committedInvoiceIds.add(invoiceModelDao.getId());
+
+ notifyOfFutureBillingEvents(entitySqlDaoWrapperFactory, invoiceModelDao.getAccountId(), callbackDateTimePerSubscriptions, context);
+
+ if (wasInvoiceCreated) {
notifyBusOfInvoiceCreation(entitySqlDaoWrapperFactory, invoiceModelDao, context);
- } else if (madeChanges) {
- // Notify the bus since the balance of the invoice changed (only if the invoice is COMMITTED)
- // TODO should we post an InvoiceCreationInternalEvent event instead? Note! This will trigger a payment (see InvoiceHandler)
- notifyBusOfInvoiceAdjustment(entitySqlDaoWrapperFactory, invoiceModelDao.getId(), invoiceModelDao.getAccountId(), context.getUserToken(), context);
}
+ } else if (wasInvoiceCreated && invoiceModelDao.isParentInvoice()) {
+ // Commit queue
+ notifyOfParentInvoiceCreation(entitySqlDaoWrapperFactory, invoiceModelDao, context);
+ }
+ }
+
+ for (final UUID adjustedInvoiceId : adjustedInvoiceIds) {
+ final boolean newInvoice = createdInvoiceIds.contains(adjustedInvoiceId);
+ if (newInvoice) {
+ // New invoice, so no associated payment yet: no need to refresh the invoice state
+ cbaDao.doCBAComplexityFromTransaction(invoiceByInvoiceId.get(adjustedInvoiceId), invoicesTags, entitySqlDaoWrapperFactory, context);
+ } else {
+ // Existing invoice (e.g. we're processing an adjustment): refresh the invoice state to get the correct balance
+ // Should we maybe enforce callers (e.g. InvoiceApiHelper) to properly populate these invoices?
+ cbaDao.doCBAComplexityFromTransaction(adjustedInvoiceId, invoicesTags, entitySqlDaoWrapperFactory, context);
+ }
+
+ if (committedInvoiceIds.contains(adjustedInvoiceId) && !newInvoice) {
+ // Notify the bus since the balance of the invoice changed (only if the invoice is COMMITTED)
+ notifyBusOfInvoiceAdjustment(entitySqlDaoWrapperFactory, adjustedInvoiceId, accountId, context.getUserToken(), context);
}
}
@@ -342,13 +379,15 @@ public class DefaultInvoiceDao extends EntityDaoBase<InvoiceModelDao, Invoice, I
@Override
public List<InvoiceModelDao> getInvoicesBySubscription(final UUID subscriptionId, final InternalTenantContext context) {
+ final List<Tag> invoicesTags = getInvoicesTags(context);
+
return transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<List<InvoiceModelDao>>() {
@Override
public List<InvoiceModelDao> inTransaction(final EntitySqlDaoWrapperFactory entitySqlDaoWrapperFactory) throws Exception {
final InvoiceSqlDao invoiceDao = entitySqlDaoWrapperFactory.become(InvoiceSqlDao.class);
final List<InvoiceModelDao> invoices = invoiceDao.getInvoicesBySubscription(subscriptionId.toString(), context);
- invoiceDaoHelper.populateChildren(invoices, entitySqlDaoWrapperFactory, context);
+ invoiceDaoHelper.populateChildren(invoices, invoicesTags, entitySqlDaoWrapperFactory, context);
return invoices;
}
@@ -378,7 +417,7 @@ public class DefaultInvoiceDao extends EntityDaoBase<InvoiceModelDao, Invoice, I
ImmutableList.<InvoiceModelDao>of(getByNumber(invoiceNumber, context)).iterator() :
invoiceSqlDao.search(searchKey, String.format("%%%s%%", searchKey), offset, limit, ordering.toString(), context);
} catch (final InvoiceApiException ignored) {
- return Iterators.<InvoiceModelDao>emptyIterator();
+ return ImmutableSet.<InvoiceModelDao>of().iterator();
}
}
},
@@ -390,13 +429,15 @@ public class DefaultInvoiceDao extends EntityDaoBase<InvoiceModelDao, Invoice, I
@Override
public BigDecimal getAccountBalance(final UUID accountId, final InternalTenantContext context) {
+ final List<Tag> invoicesTags = getInvoicesTags(context);
+
return transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<BigDecimal>() {
@Override
public BigDecimal inTransaction(final EntitySqlDaoWrapperFactory entitySqlDaoWrapperFactory) throws Exception {
BigDecimal cba = BigDecimal.ZERO;
BigDecimal accountBalance = BigDecimal.ZERO;
- final List<InvoiceModelDao> invoices = invoiceDaoHelper.getAllInvoicesByAccountFromTransaction(entitySqlDaoWrapperFactory, context);
+ final List<InvoiceModelDao> invoices = invoiceDaoHelper.getAllInvoicesByAccountFromTransaction(invoicesTags, entitySqlDaoWrapperFactory, context);
for (final InvoiceModelDao cur : invoices) {
accountBalance = accountBalance.add((new DefaultInvoice(cur)).getBalance());
cba = cba.add(InvoiceModelDaoHelper.getCBAAmount(cur));
@@ -411,17 +452,19 @@ public class DefaultInvoiceDao extends EntityDaoBase<InvoiceModelDao, Invoice, I
return transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<BigDecimal>() {
@Override
public BigDecimal inTransaction(final EntitySqlDaoWrapperFactory entitySqlDaoWrapperFactory) throws Exception {
- return cbaDao.getAccountCBAFromTransaction(accountId, entitySqlDaoWrapperFactory, context);
+ return cbaDao.getAccountCBAFromTransaction(entitySqlDaoWrapperFactory, context);
}
});
}
@Override
public List<InvoiceModelDao> getUnpaidInvoicesByAccountId(final UUID accountId, @Nullable final LocalDate upToDate, final InternalTenantContext context) {
+ final List<Tag> invoicesTags = getInvoicesTags(context);
+
return transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<List<InvoiceModelDao>>() {
@Override
public List<InvoiceModelDao> inTransaction(final EntitySqlDaoWrapperFactory entitySqlDaoWrapperFactory) throws Exception {
- return invoiceDaoHelper.getUnpaidInvoicesByAccountFromTransaction(accountId, entitySqlDaoWrapperFactory, upToDate, context);
+ return invoiceDaoHelper.getUnpaidInvoicesByAccountFromTransaction(accountId, invoicesTags, entitySqlDaoWrapperFactory, upToDate, context);
}
});
}
@@ -466,6 +509,8 @@ public class DefaultInvoiceDao extends EntityDaoBase<InvoiceModelDao, Invoice, I
throw new InvoiceApiException(ErrorCode.INVOICE_ITEMS_ADJUSTMENT_MISSING);
}
+ final List<Tag> invoicesTags = getInvoicesTags(context);
+
return transactionalSqlDao.execute(InvoiceApiException.class, new EntitySqlDaoTransactionWrapper<InvoicePaymentModelDao>() {
@Override
public InvoicePaymentModelDao inTransaction(final EntitySqlDaoWrapperFactory entitySqlDaoWrapperFactory) throws Exception {
@@ -486,6 +531,7 @@ public class DefaultInvoiceDao extends EntityDaoBase<InvoiceModelDao, Invoice, I
// Retrieve the amounts to adjust, if needed
final Map<UUID, BigDecimal> invoiceItemIdsWithAmounts = invoiceDaoHelper.computeItemAdjustments(payment.getInvoiceId().toString(),
+ invoicesTags,
entitySqlDaoWrapperFactory,
invoiceItemIdsWithNullAmounts,
context);
@@ -509,7 +555,7 @@ public class DefaultInvoiceDao extends EntityDaoBase<InvoiceModelDao, Invoice, I
// Retrieve invoice after the Refund
final InvoiceModelDao invoice = transInvoiceDao.getById(payment.getInvoiceId().toString(), context);
Preconditions.checkState(invoice != null, "Invoice shouldn't be null for payment " + payment.getId());
- invoiceDaoHelper.populateChildren(invoice, entitySqlDaoWrapperFactory, context);
+ invoiceDaoHelper.populateChildren(invoice, invoicesTags, entitySqlDaoWrapperFactory, context);
final BigDecimal invoiceBalanceAfterRefund = InvoiceModelDaoHelper.getBalance(invoice);
final InvoiceItemSqlDao transInvoiceItemDao = entitySqlDaoWrapperFactory.become(InvoiceItemSqlDao.class);
@@ -530,7 +576,8 @@ public class DefaultInvoiceDao extends EntityDaoBase<InvoiceModelDao, Invoice, I
}
}
- cbaDao.addCBAComplexityFromTransaction(invoice, entitySqlDaoWrapperFactory, context);
+ // The invoice object has been kept up-to-date
+ cbaDao.doCBAComplexityFromTransaction(invoice, invoicesTags, entitySqlDaoWrapperFactory, context);
if (isInvoiceAdjusted) {
notifyBusOfInvoiceAdjustment(entitySqlDaoWrapperFactory, invoice.getId(), invoice.getAccountId(), context.getUserToken(), context);
@@ -544,6 +591,8 @@ public class DefaultInvoiceDao extends EntityDaoBase<InvoiceModelDao, Invoice, I
@Override
public InvoicePaymentModelDao postChargeback(final UUID paymentId, final String chargebackTransactionExternalKey, final BigDecimal amount, final Currency currency, final InternalCallContext context) throws InvoiceApiException {
+ final List<Tag> invoicesTags = getInvoicesTags(context);
+
return transactionalSqlDao.execute(InvoiceApiException.class, new EntitySqlDaoTransactionWrapper<InvoicePaymentModelDao>() {
@Override
public InvoicePaymentModelDao inTransaction(final EntitySqlDaoWrapperFactory entitySqlDaoWrapperFactory) throws Exception {
@@ -586,7 +635,7 @@ public class DefaultInvoiceDao extends EntityDaoBase<InvoiceModelDao, Invoice, I
// Notify the bus since the balance of the invoice changed
final UUID accountId = transactional.getAccountIdFromInvoicePaymentId(chargeBack.getId().toString(), context);
- cbaDao.addCBAComplexityFromTransaction(payment.getInvoiceId(), entitySqlDaoWrapperFactory, context);
+ cbaDao.doCBAComplexityFromTransaction(payment.getInvoiceId(), invoicesTags, entitySqlDaoWrapperFactory, context);
notifyBusOfInvoicePayment(entitySqlDaoWrapperFactory, chargeBack, accountId, context.getUserToken(), context);
@@ -597,6 +646,8 @@ public class DefaultInvoiceDao extends EntityDaoBase<InvoiceModelDao, Invoice, I
@Override
public InvoicePaymentModelDao postChargebackReversal(final UUID paymentId, final String chargebackTransactionExternalKey, final InternalCallContext context) throws InvoiceApiException {
+ final List<Tag> invoicesTags = getInvoicesTags(context);
+
return transactionalSqlDao.execute(InvoiceApiException.class, new EntitySqlDaoTransactionWrapper<InvoicePaymentModelDao>() {
@Override
public InvoicePaymentModelDao inTransaction(final EntitySqlDaoWrapperFactory entitySqlDaoWrapperFactory) throws Exception {
@@ -622,7 +673,7 @@ public class DefaultInvoiceDao extends EntityDaoBase<InvoiceModelDao, Invoice, I
// Notify the bus since the balance of the invoice changed
final UUID accountId = transactional.getAccountIdFromInvoicePaymentId(chargebackReversed.getId().toString(), context);
- cbaDao.addCBAComplexityFromTransaction(chargebackReversed.getInvoiceId(), entitySqlDaoWrapperFactory, context);
+ cbaDao.doCBAComplexityFromTransaction(chargebackReversed.getInvoiceId(), invoicesTags, entitySqlDaoWrapperFactory, context);
notifyBusOfInvoicePayment(entitySqlDaoWrapperFactory, chargebackReversed, accountId, context.getUserToken(), context);
@@ -636,7 +687,7 @@ public class DefaultInvoiceDao extends EntityDaoBase<InvoiceModelDao, Invoice, I
return transactionalSqlDao.execute(InvoiceApiException.class, new EntitySqlDaoTransactionWrapper<InvoiceItemModelDao>() {
@Override
public InvoiceItemModelDao inTransaction(final EntitySqlDaoWrapperFactory entitySqlDaoWrapperFactory) throws Exception {
- final InvoiceItemModelDao cbaNewItem = cbaDao.computeCBAComplexity(invoice, entitySqlDaoWrapperFactory, context);
+ final InvoiceItemModelDao cbaNewItem = cbaDao.computeCBAComplexity(invoice, null, entitySqlDaoWrapperFactory, context);
return cbaNewItem;
}
});
@@ -644,10 +695,12 @@ public class DefaultInvoiceDao extends EntityDaoBase<InvoiceModelDao, Invoice, I
@Override
public Map<UUID, BigDecimal> computeItemAdjustments(final String invoiceId, final Map<UUID, BigDecimal> invoiceItemIdsWithNullAmounts, final InternalTenantContext context) throws InvoiceApiException {
+ final List<Tag> invoicesTags = getInvoicesTags(context);
+
return transactionalSqlDao.execute(InvoiceApiException.class, new EntitySqlDaoTransactionWrapper<Map<UUID, BigDecimal>>() {
@Override
public Map<UUID, BigDecimal> inTransaction(final EntitySqlDaoWrapperFactory entitySqlDaoWrapperFactory) throws Exception {
- return invoiceDaoHelper.computeItemAdjustments(invoiceId, entitySqlDaoWrapperFactory, invoiceItemIdsWithNullAmounts, context);
+ return invoiceDaoHelper.computeItemAdjustments(invoiceId, invoicesTags, entitySqlDaoWrapperFactory, invoiceItemIdsWithNullAmounts, context);
}
});
}
@@ -804,6 +857,8 @@ public class DefaultInvoiceDao extends EntityDaoBase<InvoiceModelDao, Invoice, I
@Override
public void deleteCBA(final UUID accountId, final UUID invoiceId, final UUID invoiceItemId, final InternalCallContext context) throws InvoiceApiException {
+ final List<Tag> invoicesTags = getInvoicesTags(context);
+
transactionalSqlDao.execute(InvoiceApiException.class, new EntitySqlDaoTransactionWrapper<Void>() {
@Override
public Void inTransaction(final EntitySqlDaoWrapperFactory entitySqlDaoWrapperFactory) throws Exception {
@@ -829,20 +884,20 @@ public class DefaultInvoiceDao extends EntityDaoBase<InvoiceModelDao, Invoice, I
createInvoiceItemFromTransaction(invoiceItemSqlDao, cbaAdjItem, context);
// Verify the final invoice balance is not negative
- invoiceDaoHelper.populateChildren(invoice, entitySqlDaoWrapperFactory, context);
+ invoiceDaoHelper.populateChildren(invoice, invoicesTags, entitySqlDaoWrapperFactory, context);
if (InvoiceModelDaoHelper.getBalance(invoice).compareTo(BigDecimal.ZERO) < 0) {
throw new InvoiceApiException(ErrorCode.INVOICE_WOULD_BE_NEGATIVE);
}
// If there is more account credit than CBA we adjusted, we're done.
// Otherwise, we need to find further invoices on which this credit was consumed
- final BigDecimal accountCBA = cbaDao.getAccountCBAFromTransaction(accountId, entitySqlDaoWrapperFactory, context);
+ final BigDecimal accountCBA = cbaDao.getAccountCBAFromTransaction(entitySqlDaoWrapperFactory, context);
if (accountCBA.compareTo(BigDecimal.ZERO) < 0) {
if (accountCBA.compareTo(cbaItem.getAmount().negate()) < 0) {
throw new IllegalStateException("The account balance can't be lower than the amount adjusted");
}
final List<InvoiceModelDao> invoicesFollowing = getAllNonMigratedInvoicesByAccountAfterDate(transactional, invoice.getInvoiceDate(), context);
- invoiceDaoHelper.populateChildren(invoicesFollowing, entitySqlDaoWrapperFactory, context);
+ invoiceDaoHelper.populateChildren(invoicesFollowing, invoicesTags, entitySqlDaoWrapperFactory, context);
// The remaining amount to adjust (i.e. the amount of credits used on following invoices)
// is the current account CBA balance (minus the sign)
@@ -895,12 +950,14 @@ public class DefaultInvoiceDao extends EntityDaoBase<InvoiceModelDao, Invoice, I
});
}
+ @Override
public void consumeExstingCBAOnAccountWithUnpaidInvoices(final UUID accountId, final InternalCallContext context) {
+ final List<Tag> invoicesTags = getInvoicesTags(context);
+
transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<Void>() {
@Override
public Void inTransaction(final EntitySqlDaoWrapperFactory entitySqlDaoWrapperFactory) throws Exception {
- // In theory we should only have to call useExistingCBAFromTransaction but just to be safe we also check for credit generation
- cbaDao.addCBAComplexityFromTransaction(entitySqlDaoWrapperFactory, context);
+ cbaDao.doCBAComplexityFromTransaction(invoicesTags, entitySqlDaoWrapperFactory, context);
return null;
}
});
@@ -999,6 +1056,8 @@ public class DefaultInvoiceDao extends EntityDaoBase<InvoiceModelDao, Invoice, I
@Override
public void changeInvoiceStatus(final UUID invoiceId, final InvoiceStatus newStatus,
final InternalCallContext context) throws InvoiceApiException {
+ final List<Tag> invoicesTags = getInvoicesTags(context);
+
transactionalSqlDao.execute(InvoiceApiException.class, new EntitySqlDaoTransactionWrapper<Void>() {
@Override
public Void inTransaction(final EntitySqlDaoWrapperFactory entitySqlDaoWrapperFactory) throws Exception {
@@ -1017,6 +1076,8 @@ public class DefaultInvoiceDao extends EntityDaoBase<InvoiceModelDao, Invoice, I
transactional.updateStatus(invoiceId.toString(), newStatus.toString(), context);
+ cbaDao.doCBAComplexityFromTransaction(invoicesTags, entitySqlDaoWrapperFactory, context);
+
if (InvoiceStatus.COMMITTED.equals(newStatus)) {
// notify invoice creation event
notifyBusOfInvoiceCreation(entitySqlDaoWrapperFactory, invoice, context);
@@ -1040,9 +1101,10 @@ public class DefaultInvoiceDao extends EntityDaoBase<InvoiceModelDao, Invoice, I
}
}
- private void notifyOfParentInvoiceCreation(final EntitySqlDaoWrapperFactory entitySqlDaoWrapperFactory, final InvoiceModelDao parentInvoice,
- final FutureAccountNotifications callbackDateTime, final InternalCallContext context) {
- DateTime futureNotificationDate = parentInvoice.getCreatedDate().withTimeAtStartOfDay().plusDays(1);
+ private void notifyOfParentInvoiceCreation(final EntitySqlDaoWrapperFactory entitySqlDaoWrapperFactory,
+ final InvoiceModelDao parentInvoice,
+ final InternalCallContext context) {
+ final DateTime futureNotificationDate = parentInvoice.getCreatedDate().withTimeAtStartOfDay().plusDays(1);
parentInvoiceCommitmentPoster.insertParentInvoiceFromTransactionInternal(entitySqlDaoWrapperFactory, parentInvoice.getId(), futureNotificationDate, context);
}
@@ -1071,13 +1133,15 @@ public class DefaultInvoiceDao extends EntityDaoBase<InvoiceModelDao, Invoice, I
@Override
public InvoiceModelDao getParentDraftInvoice(final UUID parentAccountId, final InternalCallContext context) throws InvoiceApiException {
+ final List<Tag> invoicesTags = getInvoicesTags(context);
+
return transactionalSqlDao.execute(InvoiceApiException.class, new EntitySqlDaoTransactionWrapper<InvoiceModelDao>() {
@Override
public InvoiceModelDao inTransaction(final EntitySqlDaoWrapperFactory entitySqlDaoWrapperFactory) throws Exception {
final InvoiceSqlDao invoiceSqlDao = entitySqlDaoWrapperFactory.become(InvoiceSqlDao.class);
InvoiceModelDao invoice = invoiceSqlDao.getParentDraftInvoice(parentAccountId.toString(), context);
if (invoice != null) {
- invoiceDaoHelper.populateChildren(invoice, entitySqlDaoWrapperFactory, context);
+ invoiceDaoHelper.populateChildren(invoice, invoicesTags, entitySqlDaoWrapperFactory, context);
}
return invoice;
}
@@ -1106,15 +1170,17 @@ public class DefaultInvoiceDao extends EntityDaoBase<InvoiceModelDao, Invoice, I
@Override
public void transferChildCreditToParent(final Account childAccount, final InternalCallContext childAccountContext) throws InvoiceApiException {
+ // Need to create an internalCallContext for parent account because it's needed to save the correct accountRecordId in Invoice tables.
+ // Then it's used to load invoices by account.
+ final InternalTenantContext internalTenantContext = internalCallContextFactory.createInternalTenantContext(childAccount.getParentAccountId(), childAccountContext);
+ final InternalCallContext parentAccountContext = internalCallContextFactory.createInternalCallContext(internalTenantContext.getAccountRecordId(), childAccountContext);
+
+ final List<Tag> parentInvoicesTags = getInvoicesTags(parentAccountContext);
+ final List<Tag> childInvoicesTags = getInvoicesTags(childAccountContext);
+
transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<Void>() {
@Override
public Void inTransaction(final EntitySqlDaoWrapperFactory entitySqlDaoWrapperFactory) throws Exception {
-
- // Need to create an internalCallContext for parent account because it's needed to save the correct accountRecordId in Invoice tables.
- // Then it's used to load invoices by account.
- final InternalTenantContext internalTenantContext = internalCallContextFactory.createInternalTenantContext(childAccount.getParentAccountId(), childAccountContext);
- final InternalCallContext parentAccountContext = internalCallContextFactory.createInternalCallContext(internalTenantContext.getAccountRecordId(), childAccountContext);
-
final InvoiceSqlDao invoiceSqlDao = entitySqlDaoWrapperFactory.become(InvoiceSqlDao.class);
final InvoiceItemSqlDao transInvoiceItemSqlDao = entitySqlDaoWrapperFactory.become(InvoiceItemSqlDao.class);
@@ -1163,19 +1229,25 @@ public class DefaultInvoiceDao extends EntityDaoBase<InvoiceModelDao, Invoice, I
// save invoices and invoice items
- InvoiceModelDao childInvoice = new InvoiceModelDao(invoiceForExternalCharge);
+ final InvoiceModelDao childInvoice = new InvoiceModelDao(invoiceForExternalCharge);
invoiceSqlDao.create(childInvoice, childAccountContext);
- createInvoiceItemFromTransaction(transInvoiceItemSqlDao, new InvoiceItemModelDao(externalChargeItem), childAccountContext);
+ final InvoiceItemModelDao childExternalChargeItem = new InvoiceItemModelDao(externalChargeItem);
+ createInvoiceItemFromTransaction(transInvoiceItemSqlDao, childExternalChargeItem, childAccountContext);
+ // Keep invoice up-to-date for CBA below
+ childInvoice.addInvoiceItem(childExternalChargeItem);
- InvoiceModelDao parentInvoice = new InvoiceModelDao(invoiceForCredit);
+ final InvoiceModelDao parentInvoice = new InvoiceModelDao(invoiceForCredit);
invoiceSqlDao.create(parentInvoice, parentAccountContext);
- createInvoiceItemFromTransaction(transInvoiceItemSqlDao, new InvoiceItemModelDao(creditItem), parentAccountContext);
+ final InvoiceItemModelDao parentCreditItem = new InvoiceItemModelDao(creditItem);
+ createInvoiceItemFromTransaction(transInvoiceItemSqlDao, parentCreditItem, parentAccountContext);
+ // Keep invoice up-to-date for CBA below
+ parentInvoice.addInvoiceItem(parentCreditItem);
// add CBA complexity and notify bus on child invoice creation
- cbaDao.addCBAComplexityFromTransaction(childInvoice.getId(), entitySqlDaoWrapperFactory, childAccountContext);
+ cbaDao.doCBAComplexityFromTransaction(childInvoice, childInvoicesTags, entitySqlDaoWrapperFactory, childAccountContext);
notifyBusOfInvoiceCreation(entitySqlDaoWrapperFactory, childInvoice, childAccountContext);
- cbaDao.addCBAComplexityFromTransaction(parentInvoice.getId(), entitySqlDaoWrapperFactory, parentAccountContext);
+ cbaDao.doCBAComplexityFromTransaction(parentInvoice, parentInvoicesTags, entitySqlDaoWrapperFactory, parentAccountContext);
notifyBusOfInvoiceCreation(entitySqlDaoWrapperFactory, parentInvoice, parentAccountContext);
return null;
@@ -1193,4 +1265,9 @@ public class DefaultInvoiceDao extends EntityDaoBase<InvoiceModelDao, Invoice, I
}
});
}
+
+ // PERF: fetch tags once. See also https://github.com/killbill/killbill/issues/720.
+ private List<Tag> getInvoicesTags(final InternalTenantContext context) {
+ return tagInternalApi.getTagsForAccountType(ObjectType.INVOICE, false, context);
+ }
}
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/dao/InvoiceDao.java b/invoice/src/main/java/org/killbill/billing/invoice/dao/InvoiceDao.java
index ca7c49b..377b150 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/dao/InvoiceDao.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/dao/InvoiceDao.java
@@ -1,7 +1,7 @@
/*
* Copyright 2010-2013 Ning, Inc.
- * Copyright 2014-2016 Groupon, Inc
- * Copyright 2014-2016 The Billing Project, LLC
+ * Copyright 2014-2017 Groupon, Inc
+ * Copyright 2014-2017 The Billing Project, LLC
*
* The Billing Project licenses this file to you under the Apache License, version 2.0
* (the "License"); you may not use this file except in compliance with the
@@ -39,15 +39,15 @@ import org.killbill.billing.util.entity.dao.EntityDao;
public interface InvoiceDao extends EntityDao<InvoiceModelDao, Invoice, InvoiceApiException> {
- void createInvoice(final InvoiceModelDao invoice, final List<InvoiceItemModelDao> invoiceItems,
- final boolean isRealInvoice, final FutureAccountNotifications callbackDateTimePerSubscriptions,
+ void createInvoice(final InvoiceModelDao invoice,
+ final FutureAccountNotifications callbackDateTimePerSubscriptions,
final InternalCallContext context);
+ List<InvoiceItemModelDao> createInvoices(final List<InvoiceModelDao> invoices, final InternalCallContext context);
+
public void setFutureAccountNotificationsForEmptyInvoice(final UUID accountId, final FutureAccountNotifications callbackDateTimePerSubscriptions,
final InternalCallContext context);
- List<InvoiceItemModelDao> createInvoices(final List<InvoiceModelDao> invoices, final InternalCallContext context);
-
InvoiceModelDao getByNumber(Integer number, InternalTenantContext context) throws InvoiceApiException;
List<InvoiceModelDao> getInvoicesByAccount(InternalTenantContext context);
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/dao/InvoiceDaoHelper.java b/invoice/src/main/java/org/killbill/billing/invoice/dao/InvoiceDaoHelper.java
index 27168cf..cfd92a1 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/dao/InvoiceDaoHelper.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/dao/InvoiceDaoHelper.java
@@ -40,7 +40,6 @@ import org.killbill.billing.catalog.api.Currency;
import org.killbill.billing.invoice.api.InvoiceApiException;
import org.killbill.billing.invoice.api.InvoiceItemType;
import org.killbill.billing.invoice.api.InvoiceStatus;
-import org.killbill.billing.tag.TagInternalApi;
import org.killbill.billing.util.callcontext.InternalCallContextFactory;
import org.killbill.billing.util.entity.dao.EntitySqlDaoWrapperFactory;
import org.killbill.billing.util.tag.ControlTagType;
@@ -48,8 +47,7 @@ import org.killbill.billing.util.tag.Tag;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import com.google.common.base.Functions;
-import com.google.common.base.Objects;
+import com.google.common.base.MoreObjects;
import com.google.common.base.Predicate;
import com.google.common.collect.Collections2;
import com.google.common.collect.ImmutableList;
@@ -59,12 +57,10 @@ public class InvoiceDaoHelper {
private static final Logger log = LoggerFactory.getLogger(InvoiceDaoHelper.class);
- private final TagInternalApi tagInternalApi;
private final InternalCallContextFactory internalCallContextFactory;
@Inject
- public InvoiceDaoHelper(final TagInternalApi tagInternalApi, final InternalCallContextFactory internalCallContextFactory) {
- this.tagInternalApi = tagInternalApi;
+ public InvoiceDaoHelper(final InternalCallContextFactory internalCallContextFactory) {
this.internalCallContextFactory = internalCallContextFactory;
}
@@ -82,6 +78,7 @@ public class InvoiceDaoHelper {
* @throws org.killbill.billing.invoice.api.InvoiceApiException
*/
public Map<UUID, BigDecimal> computeItemAdjustments(final String invoiceId,
+ final List<Tag> invoicesTags,
final EntitySqlDaoWrapperFactory entitySqlDaoWrapperFactory,
final Map<UUID, BigDecimal> invoiceItemIdsWithNullAmounts,
final InternalTenantContext context) throws InvoiceApiException {
@@ -90,7 +87,7 @@ public class InvoiceDaoHelper {
// Retrieve invoice before the Refund
final InvoiceModelDao invoice = entitySqlDaoWrapperFactory.become(InvoiceSqlDao.class).getById(invoiceId, context);
if (invoice != null) {
- populateChildren(invoice, entitySqlDaoWrapperFactory, context);
+ populateChildren(invoice, invoicesTags, entitySqlDaoWrapperFactory, context);
} else {
throw new IllegalStateException("Invoice shouldn't be null for id " + invoiceId);
}
@@ -114,7 +111,7 @@ public class InvoiceDaoHelper {
throw new InvoiceApiException(ErrorCode.INVOICE_ITEM_ADJUSTMENT_AMOUNT_INVALID, proposedItemAmount, maxAdjLeftAmount);
}
- final BigDecimal itemAmountToAdjust = Objects.firstNonNull(proposedItemAmount, maxAdjLeftAmount);
+ final BigDecimal itemAmountToAdjust = MoreObjects.firstNonNull(proposedItemAmount, maxAdjLeftAmount);
if (itemAmountToAdjust.compareTo(BigDecimal.ZERO) > 0) {
outputAdjInvoiceItem.put(targetInvoiceItem.getId(), itemAmountToAdjust);
}
@@ -167,8 +164,8 @@ public class InvoiceDaoHelper {
return requestedPositiveAmount;
}
- public List<InvoiceModelDao> getUnpaidInvoicesByAccountFromTransaction(final UUID accountId, final EntitySqlDaoWrapperFactory entitySqlDaoWrapperFactory, final LocalDate upToDate, final InternalTenantContext context) {
- final List<InvoiceModelDao> invoices = getAllInvoicesByAccountFromTransaction(entitySqlDaoWrapperFactory, context);
+ public List<InvoiceModelDao> getUnpaidInvoicesByAccountFromTransaction(final UUID accountId, final List<Tag> invoicesTags, final EntitySqlDaoWrapperFactory entitySqlDaoWrapperFactory, final LocalDate upToDate, final InternalTenantContext context) {
+ final List<InvoiceModelDao> invoices = getAllInvoicesByAccountFromTransaction(invoicesTags, entitySqlDaoWrapperFactory, context);
log.debug("Found invoices={} for accountId={}", invoices, accountId);
return getUnpaidInvoicesByAccountFromTransaction(invoices, upToDate);
}
@@ -214,9 +211,9 @@ public class InvoiceDaoHelper {
}
// Retrieve the amount and currency if needed
- final BigDecimal amountToAdjust = Objects.firstNonNull(positiveAdjAmount, invoiceItemToBeAdjusted.getAmount());
+ final BigDecimal amountToAdjust = MoreObjects.firstNonNull(positiveAdjAmount, invoiceItemToBeAdjusted.getAmount());
// TODO - should we enforce the currency (and respect the original one) here if the amount passed was null?
- final Currency currencyForAdjustment = Objects.firstNonNull(currency, invoiceItemToBeAdjusted.getCurrency());
+ final Currency currencyForAdjustment = MoreObjects.firstNonNull(currency, invoiceItemToBeAdjusted.getCurrency());
// Finally, create the adjustment
// Note! The amount is negated here!
@@ -224,18 +221,18 @@ public class InvoiceDaoHelper {
null, null, null, null, null, null, effectiveDate, effectiveDate, amountToAdjust.negate(), null, currencyForAdjustment, invoiceItemToBeAdjusted.getId());
}
- public void populateChildren(final InvoiceModelDao invoice, final EntitySqlDaoWrapperFactory entitySqlDaoWrapperFactory, final InternalTenantContext context) {
- populateChildren(ImmutableList.<InvoiceModelDao>of(invoice), entitySqlDaoWrapperFactory, context);
+ public void populateChildren(final InvoiceModelDao invoice, final List<Tag> invoicesTags, final EntitySqlDaoWrapperFactory entitySqlDaoWrapperFactory, final InternalTenantContext context) {
+ populateChildren(ImmutableList.<InvoiceModelDao>of(invoice), invoicesTags, entitySqlDaoWrapperFactory, context);
}
- public void populateChildren(final Iterable<InvoiceModelDao> invoices, final EntitySqlDaoWrapperFactory entitySqlDaoWrapperFactory, final InternalTenantContext context) {
+ public void populateChildren(final Iterable<InvoiceModelDao> invoices, final List<Tag> invoicesTags, final EntitySqlDaoWrapperFactory entitySqlDaoWrapperFactory, final InternalTenantContext context) {
if (Iterables.<InvoiceModelDao>isEmpty(invoices)) {
return;
}
getInvoiceItemsWithinTransaction(invoices, entitySqlDaoWrapperFactory, context);
getInvoicePaymentsWithinTransaction(invoices, entitySqlDaoWrapperFactory, context);
- setInvoicesWrittenOff(invoices, context);
+ setInvoicesWrittenOff(invoices, invoicesTags);
final Iterable<InvoiceModelDao> nonParentInvoices = Iterables.<InvoiceModelDao>filter(invoices,
new Predicate<InvoiceModelDao>() {
@@ -246,14 +243,15 @@ public class InvoiceDaoHelper {
});
if (!Iterables.<InvoiceModelDao>isEmpty(nonParentInvoices)) {
setParentInvoice(nonParentInvoices,
+ invoicesTags,
entitySqlDaoWrapperFactory,
context);
}
}
- public List<InvoiceModelDao> getAllInvoicesByAccountFromTransaction(final EntitySqlDaoWrapperFactory entitySqlDaoWrapperFactory, final InternalTenantContext context) {
+ public List<InvoiceModelDao> getAllInvoicesByAccountFromTransaction(final List<Tag> invoicesTags, final EntitySqlDaoWrapperFactory entitySqlDaoWrapperFactory, final InternalTenantContext context) {
final List<InvoiceModelDao> invoices = entitySqlDaoWrapperFactory.become(InvoiceSqlDao.class).getByAccountRecordId(context);
- populateChildren(invoices, entitySqlDaoWrapperFactory, context);
+ populateChildren(invoices, invoicesTags, entitySqlDaoWrapperFactory, context);
return invoices;
}
@@ -276,7 +274,7 @@ public class InvoiceDaoHelper {
for (final InvoiceModelDao invoice : invoices) {
// Make sure to set invoice items to a non-null value
- final List<InvoiceItemModelDao> invoiceItemsForInvoice = Objects.firstNonNull(invoiceItemsPerInvoiceId.get(invoice.getId()), ImmutableList.<InvoiceItemModelDao>of());
+ final List<InvoiceItemModelDao> invoiceItemsForInvoice = MoreObjects.firstNonNull(invoiceItemsPerInvoiceId.get(invoice.getId()), ImmutableList.<InvoiceItemModelDao>of());
log.debug("Found items={} for invoice={}", invoiceItemsForInvoice, invoice);
invoice.addInvoiceItems(invoiceItemsForInvoice);
}
@@ -296,7 +294,7 @@ public class InvoiceDaoHelper {
for (final InvoiceModelDao invoice : invoices) {
// Make sure to set payments to a non-null value
- final List<InvoicePaymentModelDao> invoicePaymentsForInvoice = Objects.firstNonNull(invoicePaymentsPerInvoiceId.get(invoice.getId()), ImmutableList.<InvoicePaymentModelDao>of());
+ final List<InvoicePaymentModelDao> invoicePaymentsForInvoice = MoreObjects.firstNonNull(invoicePaymentsPerInvoiceId.get(invoice.getId()), ImmutableList.<InvoicePaymentModelDao>of());
log.debug("Found payments={} for invoice={}", invoicePaymentsForInvoice, invoice);
invoice.addPayments(invoicePaymentsForInvoice);
@@ -310,9 +308,8 @@ public class InvoiceDaoHelper {
}
}
- private void setInvoicesWrittenOff(final Iterable<InvoiceModelDao> invoices, final InternalTenantContext internalTenantContext) {
- final List<Tag> tags = tagInternalApi.getTagsForAccountType(ObjectType.INVOICE, false, internalTenantContext);
- final Iterable<Tag> writtenOffTags = filterForWrittenOff(tags);
+ private void setInvoicesWrittenOff(final Iterable<InvoiceModelDao> invoices, final List<Tag> invoicesTags) {
+ final Iterable<Tag> writtenOffTags = filterForWrittenOff(invoicesTags);
for (final Tag cur : writtenOffTags) {
final InvoiceModelDao foundInvoice = Iterables.tryFind(invoices, new Predicate<InvoiceModelDao>() {
@Override
@@ -335,7 +332,7 @@ public class InvoiceDaoHelper {
});
}
- private void setParentInvoice(final Iterable<InvoiceModelDao> childInvoices, final EntitySqlDaoWrapperFactory entitySqlDaoWrapperFactory, final InternalTenantContext childContext) {
+ private void setParentInvoice(final Iterable<InvoiceModelDao> childInvoices, final List<Tag> invoicesTags, final EntitySqlDaoWrapperFactory entitySqlDaoWrapperFactory, final InternalTenantContext childContext) {
final Collection<String> childInvoiceIds = new HashSet<String>();
for (final InvoiceModelDao childInvoice : childInvoices) {
childInvoiceIds.add(childInvoice.getId().toString());
@@ -378,7 +375,7 @@ public class InvoiceDaoHelper {
final Long parentAccountRecordId = internalCallContextFactory.getRecordIdFromObject(parentAccountId, ObjectType.ACCOUNT, internalCallContextFactory.createTenantContext(childContext));
final InternalTenantContext parentContext = internalCallContextFactory.createInternalTenantContext(childContext.getTenantRecordId(), parentAccountRecordId);
// Note the misnomer here, populateChildren simply populates the content of these invoices (unrelated to HA)
- populateChildren(parentInvoicesForOneParentAccountId, entitySqlDaoWrapperFactory, parentContext);
+ populateChildren(parentInvoicesForOneParentAccountId, invoicesTags, entitySqlDaoWrapperFactory, parentContext);
}
for (final InvoiceModelDao invoice : childInvoices) {
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/dao/InvoiceItemModelDao.java b/invoice/src/main/java/org/killbill/billing/invoice/dao/InvoiceItemModelDao.java
index 8805d52..e814a69 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/dao/InvoiceItemModelDao.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/dao/InvoiceItemModelDao.java
@@ -183,6 +183,10 @@ public class InvoiceItemModelDao extends EntityModelDaoBase implements EntityMod
this.subscriptionId = subscriptionId;
}
+ public void setDescription(final String description) {
+ this.description = description;
+ }
+
public void setPlanName(final String planName) {
this.planName = planName;
}
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/dao/InvoiceItemSqlDao.java b/invoice/src/main/java/org/killbill/billing/invoice/dao/InvoiceItemSqlDao.java
index e30b5d2..8c7ded7 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/dao/InvoiceItemSqlDao.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/dao/InvoiceItemSqlDao.java
@@ -1,7 +1,9 @@
/*
* Copyright 2010-2013 Ning, Inc.
+ * Copyright 2014-2017 Groupon, Inc
+ * Copyright 2014-2017 The Billing Project, LLC
*
- * Ning licenses this file to you under the Apache License, version 2.0
+ * The Billing Project licenses this file to you under the Apache License, version 2.0
* (the "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at:
*
@@ -20,16 +22,15 @@ import java.math.BigDecimal;
import java.util.List;
import org.killbill.billing.callcontext.InternalCallContext;
+import org.killbill.billing.callcontext.InternalTenantContext;
+import org.killbill.billing.invoice.api.InvoiceItem;
import org.killbill.billing.util.audit.ChangeType;
import org.killbill.billing.util.entity.dao.Audited;
+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.killbill.billing.invoice.api.InvoiceItem;
-import org.killbill.billing.callcontext.InternalTenantContext;
-import org.killbill.billing.util.entity.dao.EntitySqlDao;
-import org.killbill.billing.util.entity.dao.EntitySqlDaoStringTemplate;
import org.skife.jdbi.v2.sqlobject.SqlUpdate;
@EntitySqlDaoStringTemplate
@@ -57,4 +58,7 @@ public interface InvoiceItemSqlDao extends EntitySqlDao<InvoiceItemModelDao, Inv
@SqlQuery
List<InvoiceItemModelDao> getInvoiceItemsByParentInvoice(@Bind("parentInvoiceId") final String parentInvoiceId,
@BindBean final InternalTenantContext context);
+
+ @SqlQuery
+ BigDecimal getAccountCBA(@BindBean final InternalTenantContext context);
}
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/generator/DefaultInvoiceGenerator.java b/invoice/src/main/java/org/killbill/billing/invoice/generator/DefaultInvoiceGenerator.java
index 1e64bb6..33f7c25 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/generator/DefaultInvoiceGenerator.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/generator/DefaultInvoiceGenerator.java
@@ -77,7 +77,7 @@ public class DefaultInvoiceGenerator implements InvoiceGenerator {
final LocalDate adjustedTargetDate = adjustTargetDate(existingInvoices, targetDate);
final LocalDate invoiceDate = context.toLocalDate(context.getCreatedDate());
- final Invoice invoice = new DefaultInvoice(account.getId(), invoiceDate, adjustedTargetDate, targetCurrency);
+ final DefaultInvoice invoice = new DefaultInvoice(account.getId(), invoiceDate, adjustedTargetDate, targetCurrency);
final UUID invoiceId = invoice.getId();
final Map<UUID, SubscriptionFutureNotificationDates> perSubscriptionFutureNotificationDates = new HashMap<UUID, SubscriptionFutureNotificationDates>();
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/generator/InvoiceWithMetadata.java b/invoice/src/main/java/org/killbill/billing/invoice/generator/InvoiceWithMetadata.java
index 3473252..746cdf1 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/generator/InvoiceWithMetadata.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/generator/InvoiceWithMetadata.java
@@ -31,6 +31,7 @@ import org.killbill.billing.catalog.api.BillingMode;
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.DefaultInvoice;
import com.google.common.base.Predicate;
import com.google.common.collect.Iterables;
@@ -39,16 +40,16 @@ public class InvoiceWithMetadata {
private final Map<UUID, SubscriptionFutureNotificationDates> perSubscriptionFutureNotificationDates;
- private Invoice invoice;
+ private DefaultInvoice invoice;
- public InvoiceWithMetadata(final Invoice originalInvoice, final Map<UUID, SubscriptionFutureNotificationDates> perSubscriptionFutureNotificationDates) {
+ public InvoiceWithMetadata(final DefaultInvoice originalInvoice, final Map<UUID, SubscriptionFutureNotificationDates> perSubscriptionFutureNotificationDates) {
this.invoice = originalInvoice;
this.perSubscriptionFutureNotificationDates = perSubscriptionFutureNotificationDates;
build();
remove$0UsageItems();
}
- public Invoice getInvoice() {
+ public DefaultInvoice getInvoice() {
return invoice;
}
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/InvoiceDispatcher.java b/invoice/src/main/java/org/killbill/billing/invoice/InvoiceDispatcher.java
index cc85579..a300bf1 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/InvoiceDispatcher.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/InvoiceDispatcher.java
@@ -52,8 +52,6 @@ import org.killbill.billing.catalog.api.PlanPhaseSpecifier;
import org.killbill.billing.entitlement.api.SubscriptionEventType;
import org.killbill.billing.events.BusInternalEvent;
import org.killbill.billing.events.EffectiveSubscriptionInternalEvent;
-import org.killbill.billing.events.InvoiceAdjustmentInternalEvent;
-import org.killbill.billing.events.InvoiceInternalEvent;
import org.killbill.billing.events.InvoiceNotificationInternalEvent;
import org.killbill.billing.invoice.InvoiceDispatcher.FutureAccountNotifications.SubscriptionNotification;
import org.killbill.billing.invoice.api.DefaultInvoiceService;
@@ -65,8 +63,6 @@ import org.killbill.billing.invoice.api.InvoiceItem;
import org.killbill.billing.invoice.api.InvoiceItemType;
import org.killbill.billing.invoice.api.InvoiceNotifier;
import org.killbill.billing.invoice.api.InvoiceStatus;
-import org.killbill.billing.invoice.api.user.DefaultInvoiceAdjustmentEvent;
-import org.killbill.billing.invoice.api.user.DefaultInvoiceCreationEvent;
import org.killbill.billing.invoice.api.user.DefaultInvoiceNotificationInternalEvent;
import org.killbill.billing.invoice.api.user.DefaultNullInvoiceEvent;
import org.killbill.billing.invoice.calculator.InvoiceCalculatorUtils;
@@ -119,7 +115,6 @@ import com.google.common.base.Preconditions;
import com.google.common.base.Predicate;
import com.google.common.collect.Collections2;
import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.collect.Ordering;
@@ -388,7 +383,7 @@ public class InvoiceDispatcher {
final Currency targetCurrency = account.getCurrency();
final InvoiceWithMetadata invoiceWithMetadata = generator.generateInvoice(account, billingEvents, invoices, targetDate, targetCurrency, context);
- final Invoice invoice = invoiceWithMetadata.getInvoice();
+ final DefaultInvoice invoice = invoiceWithMetadata.getInvoice();
// Compute future notifications
final FutureAccountNotifications futureAccountNotifications = createNextFutureNotificationDate(invoiceWithMetadata, context);
@@ -406,22 +401,37 @@ public class InvoiceDispatcher {
final BusInternalEvent event = new DefaultNullInvoiceEvent(accountId, clock.getUTCToday(),
context.getAccountRecordId(), context.getTenantRecordId(), context.getUserToken());
- commitInvoiceAndSetFutureNotifications(account, null, ImmutableList.<InvoiceItemModelDao>of(), futureAccountNotifications, false, context);
+ commitInvoiceAndSetFutureNotifications(account, null, futureAccountNotifications, context);
postEvent(event);
}
return null;
}
// Generate missing credit (> 0 for generation and < 0 for use) prior we call the plugin
- final InvoiceItem cbaItem = computeCBAOnExistingInvoice(invoice, context);
- if (cbaItem != null) {
- invoice.addInvoiceItem(cbaItem);
+ final InvoiceItem cbaItemPreInvoicePlugins = computeCBAOnExistingInvoice(invoice, context);
+ DefaultInvoice tmpInvoiceForInvoicePlugins = invoice;
+ if (cbaItemPreInvoicePlugins != null) {
+ tmpInvoiceForInvoicePlugins = (DefaultInvoice) tmpInvoiceForInvoicePlugins.clone();
+ tmpInvoiceForInvoicePlugins.addInvoiceItem(cbaItemPreInvoicePlugins);
}
//
// Ask external invoice plugins if additional items (tax, etc) shall be added to the invoice
//
final CallContext callContext = buildCallContext(context);
- invoice.addInvoiceItems(invoicePluginDispatcher.getAdditionalInvoiceItems(invoice, isDryRun, callContext));
+ final List<InvoiceItem> additionalInvoiceItemsFromPlugins = invoicePluginDispatcher.getAdditionalInvoiceItems(tmpInvoiceForInvoicePlugins, isDryRun, callContext);
+ if (additionalInvoiceItemsFromPlugins.isEmpty()) {
+ // PERF: avoid re-computing the CBA if no change was made
+ if (cbaItemPreInvoicePlugins != null) {
+ invoice.addInvoiceItem(cbaItemPreInvoicePlugins);
+ }
+ } else {
+ invoice.addInvoiceItems(additionalInvoiceItemsFromPlugins);
+ // Use credit after we call the plugin (https://github.com/killbill/killbill/issues/637)
+ final InvoiceItem cbaItemPostInvoicePlugins = computeCBAOnExistingInvoice(invoice, context);
+ if (cbaItemPostInvoicePlugins != null) {
+ invoice.addInvoiceItem(cbaItemPostInvoicePlugins);
+ }
+ }
if (!isDryRun) {
@@ -434,18 +444,17 @@ public class InvoiceDispatcher {
// Transformation to Invoice -> InvoiceModelDao
final InvoiceModelDao invoiceModelDao = new InvoiceModelDao(invoice);
- final Iterable<InvoiceItemModelDao> invoiceItemModelDaos = transformToInvoiceModelDao(invoice.getInvoiceItems());
+ final List<InvoiceItemModelDao> invoiceItemModelDaos = transformToInvoiceModelDao(invoice.getInvoiceItems());
+ invoiceModelDao.addInvoiceItems(invoiceItemModelDaos);
// Commit invoice on disk
- final boolean isThereAnyItemsLeft = commitInvoiceAndSetFutureNotifications(account, invoiceModelDao, invoiceItemModelDaos, futureAccountNotifications, isRealInvoiceWithItems, context);
+ final boolean isThereAnyItemsLeft = commitInvoiceAndSetFutureNotifications(account, invoiceModelDao, futureAccountNotifications, context);
final boolean isRealInvoiceWithNonEmptyItems = isThereAnyItemsLeft ? isRealInvoiceWithItems : false;
setChargedThroughDates(invoice.getInvoiceItems(FixedPriceInvoiceItem.class), invoice.getInvoiceItems(RecurringInvoiceItem.class), context);
if (InvoiceStatus.COMMITTED.equals(invoice.getStatus())) {
- // TODO we should send bus events when we commit the invoice on disk in commitInvoice
- postEvents(account, invoice, adjustedUniqueOtherInvoiceId, isRealInvoiceWithNonEmptyItems, context);
notifyAccountIfEnabled(account, invoice, isRealInvoiceWithNonEmptyItems, context);
}
@@ -502,8 +511,8 @@ public class InvoiceDispatcher {
return new FutureAccountNotifications(result);
}
- private Iterable<InvoiceItemModelDao> transformToInvoiceModelDao(final List<InvoiceItem> invoiceItems) {
- return Iterables.transform(invoiceItems,
+ private List<InvoiceItemModelDao> transformToInvoiceModelDao(final List<InvoiceItem> invoiceItems) {
+ return Lists.transform(invoiceItems,
new Function<InvoiceItem, InvoiceItemModelDao>() {
@Override
public InvoiceItemModelDao apply(final InvoiceItem input) {
@@ -539,37 +548,19 @@ public class InvoiceDispatcher {
log.info(tmp.toString());
}
- private boolean commitInvoiceAndSetFutureNotifications(final ImmutableAccountData account, final InvoiceModelDao invoiceModelDao,
- final Iterable<InvoiceItemModelDao> invoiceItemModelDaos,
+ private boolean commitInvoiceAndSetFutureNotifications(final ImmutableAccountData account,
+ @Nullable final InvoiceModelDao invoiceModelDao,
final FutureAccountNotifications futureAccountNotifications,
- final boolean isRealInvoiceWithItems, final InternalCallContext context) throws SubscriptionBaseApiException, InvoiceApiException {
- final boolean isThereAnyItemsLeft = invoiceItemModelDaos.iterator().hasNext();
+ final InternalCallContext context) throws SubscriptionBaseApiException, InvoiceApiException {
+ final boolean isThereAnyItemsLeft = invoiceModelDao != null && !invoiceModelDao.getInvoiceItems().isEmpty();
if (isThereAnyItemsLeft) {
- invoiceDao.createInvoice(invoiceModelDao, ImmutableList.copyOf(invoiceItemModelDaos), isRealInvoiceWithItems, futureAccountNotifications, context);
+ invoiceDao.createInvoice(invoiceModelDao, futureAccountNotifications, context);
} else {
invoiceDao.setFutureAccountNotificationsForEmptyInvoice(account.getId(), futureAccountNotifications, context);
}
return isThereAnyItemsLeft;
}
- private void postEvents(final ImmutableAccountData account, final Invoice invoice, final Set<UUID> adjustedUniqueOtherInvoiceId, final boolean isRealInvoiceWithNonEmptyItems, final InternalCallContext context) {
-
- final List<InvoiceInternalEvent> events = new ArrayList<InvoiceInternalEvent>();
- if (isRealInvoiceWithNonEmptyItems) {
- events.add(new DefaultInvoiceCreationEvent(invoice.getId(), invoice.getAccountId(),
- invoice.getBalance(), invoice.getCurrency(),
- context.getAccountRecordId(), context.getTenantRecordId(), context.getUserToken()));
- }
- for (final UUID cur : adjustedUniqueOtherInvoiceId) {
- final InvoiceAdjustmentInternalEvent event = new DefaultInvoiceAdjustmentEvent(cur, invoice.getAccountId(),
- context.getAccountRecordId(), context.getTenantRecordId(), context.getUserToken());
- events.add(event);
- }
- for (final InvoiceInternalEvent event : events) {
- postEvent(event);
- }
- }
-
private void notifyAccountIfEnabled(final ImmutableAccountData account, final Invoice invoice, final boolean isRealInvoiceWithNonEmptyItems, final InternalCallContext context) throws InvoiceApiException, AccountApiException {
// Ideally we would retrieve the cached version, all the invoice code has been modified to only use ImmutableAccountData, except for the
// isNotifiedForInvoice piece that should probably live outside of invoice code anyways... (see https://github.com/killbill/killbill-email-notifications-plugin)
@@ -823,11 +814,8 @@ public class InvoiceDispatcher {
final InvoiceItem parentInvoiceItem = new ParentInvoiceItem(UUID.randomUUID(), context.getCreatedDate(), draftParentInvoice.getId(), childAccount.getParentAccountId(), childAccount.getId(), childInvoiceAmount, childAccount.getCurrency(), description);
draftParentInvoice.addInvoiceItem(new InvoiceItemModelDao(parentInvoiceItem));
- // build account date time zone
- final FutureAccountNotifications futureAccountNotifications = new FutureAccountNotifications(ImmutableMap.<UUID, List<SubscriptionNotification>>of());
-
log.info("Adding new itemId='{}', amount='{}' on new DRAFT invoiceId='{}'", parentInvoiceItem.getId(), childInvoiceAmount, draftParentInvoice.getId());
- invoiceDao.createInvoice(draftParentInvoice, draftParentInvoice.getInvoiceItems(), true, futureAccountNotifications, parentContext);
+ invoiceDao.createInvoices(ImmutableList.<InvoiceModelDao>of(draftParentInvoice), parentContext);
}
// save parent child invoice relation
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/model/DefaultInvoice.java b/invoice/src/main/java/org/killbill/billing/invoice/model/DefaultInvoice.java
index 5b200f4..89f60a5 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/model/DefaultInvoice.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/model/DefaultInvoice.java
@@ -119,7 +119,6 @@ public class DefaultInvoice extends EntityBase implements Invoice, Cloneable {
this.parentInvoice = (parentInvoice != null) ? new DefaultInvoice(parentInvoice) : null;
}
-
// Semi deep copy where we copy the lists but not the elements in the lists since they are immutables.
@Override
public Object clone() {
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/model/ItemAdjInvoiceItem.java b/invoice/src/main/java/org/killbill/billing/invoice/model/ItemAdjInvoiceItem.java
index 13656ed..a8ffffc 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/model/ItemAdjInvoiceItem.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/model/ItemAdjInvoiceItem.java
@@ -30,7 +30,7 @@ import org.killbill.billing.invoice.api.InvoiceItem;
import org.killbill.billing.invoice.api.InvoiceItemType;
import org.killbill.billing.util.UUIDs;
-import com.google.common.base.Objects;
+import com.google.common.base.MoreObjects;
public class ItemAdjInvoiceItem extends AdjInvoiceItem {
@@ -53,6 +53,6 @@ public class ItemAdjInvoiceItem extends AdjInvoiceItem {
@Override
public String getDescription() {
- return Objects.firstNonNull(description, "Invoice item adjustment");
+ return MoreObjects.firstNonNull(description, "Invoice item adjustment");
}
}
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/model/RecurringInvoiceItem.java b/invoice/src/main/java/org/killbill/billing/invoice/model/RecurringInvoiceItem.java
index b63271b..f2a4b87 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/model/RecurringInvoiceItem.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/model/RecurringInvoiceItem.java
@@ -29,7 +29,7 @@ import org.killbill.billing.catalog.api.Currency;
import org.killbill.billing.invoice.api.InvoiceItemType;
import org.killbill.billing.util.UUIDs;
-import com.google.common.base.Objects;
+import com.google.common.base.MoreObjects;
public class RecurringInvoiceItem extends InvoiceItemBase {
@@ -53,7 +53,7 @@ public class RecurringInvoiceItem extends InvoiceItemBase {
@Override
public String getDescription() {
- return Objects.firstNonNull(description, phaseName);
+ return MoreObjects.firstNonNull(description, phaseName);
}
@Override
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/model/RepairAdjInvoiceItem.java b/invoice/src/main/java/org/killbill/billing/invoice/model/RepairAdjInvoiceItem.java
index 348fdc0..2eca25f 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/model/RepairAdjInvoiceItem.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/model/RepairAdjInvoiceItem.java
@@ -29,7 +29,7 @@ import org.killbill.billing.catalog.api.Currency;
import org.killbill.billing.invoice.api.InvoiceItemType;
import org.killbill.billing.util.UUIDs;
-import com.google.common.base.Objects;
+import com.google.common.base.MoreObjects;
public class RepairAdjInvoiceItem extends AdjInvoiceItem {
@@ -55,6 +55,6 @@ public class RepairAdjInvoiceItem extends AdjInvoiceItem {
@Override
public String getDescription() {
- return Objects.firstNonNull(description, "Adjustment (subscription change)");
+ return MoreObjects.firstNonNull(description, "Adjustment (subscription change)");
}
}
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/template/formatters/DefaultInvoiceItemFormatter.java b/invoice/src/main/java/org/killbill/billing/invoice/template/formatters/DefaultInvoiceItemFormatter.java
index cbcc8c6..47e18a3 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/template/formatters/DefaultInvoiceItemFormatter.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/template/formatters/DefaultInvoiceItemFormatter.java
@@ -37,7 +37,7 @@ import org.killbill.billing.util.template.translation.DefaultCatalogTranslator;
import org.killbill.billing.util.template.translation.Translator;
import org.killbill.billing.util.template.translation.TranslatorConfig;
-import com.google.common.base.Objects;
+import com.google.common.base.MoreObjects;
import com.google.common.base.Strings;
/**
@@ -67,7 +67,7 @@ public class DefaultInvoiceItemFormatter implements InvoiceItemFormatter {
@Override
public BigDecimal getAmount() {
- return Objects.firstNonNull(item.getAmount(), BigDecimal.ZERO);
+ return MoreObjects.firstNonNull(item.getAmount(), BigDecimal.ZERO);
}
@Override
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/tree/Item.java b/invoice/src/main/java/org/killbill/billing/invoice/tree/Item.java
index aaec4eb..4f778b6 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/tree/Item.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/tree/Item.java
@@ -31,7 +31,7 @@ import org.killbill.billing.invoice.model.RecurringInvoiceItem;
import org.killbill.billing.invoice.model.RepairAdjInvoiceItem;
import com.fasterxml.jackson.annotation.JsonIgnore;
-import com.google.common.base.Objects;
+import com.google.common.base.MoreObjects;
import com.google.common.base.Preconditions;
/**
@@ -200,9 +200,9 @@ public class Item {
// following conditions: same type, subscription, start date. Depending on the catalog configuration, the end
// date check could also match (e.g. repair from annual to monthly). For that scenario, we need to default
// to catalog checks (the rate check is a lame check for versioned catalogs).
- Objects.firstNonNull(planName, "").equals(Objects.firstNonNull(otherItem.getPlanName(), "")) &&
- Objects.firstNonNull(phaseName, "").equals(Objects.firstNonNull(otherItem.getPhaseName(), "")) &&
- Objects.firstNonNull(rate, BigDecimal.ZERO).compareTo(Objects.firstNonNull(otherItem.getRate(), BigDecimal.ZERO)) == 0;
+ MoreObjects.firstNonNull(planName, "").equals(MoreObjects.firstNonNull(otherItem.getPlanName(), "")) &&
+ MoreObjects.firstNonNull(phaseName, "").equals(MoreObjects.firstNonNull(otherItem.getPhaseName(), "")) &&
+ MoreObjects.firstNonNull(rate, BigDecimal.ZERO).compareTo(MoreObjects.firstNonNull(otherItem.getRate(), BigDecimal.ZERO)) == 0;
}
@Override
diff --git a/invoice/src/main/resources/org/killbill/billing/invoice/dao/InvoiceItemSqlDao.sql.stg b/invoice/src/main/resources/org/killbill/billing/invoice/dao/InvoiceItemSqlDao.sql.stg
index ae19a63..c99d95c 100644
--- a/invoice/src/main/resources/org/killbill/billing/invoice/dao/InvoiceItemSqlDao.sql.stg
+++ b/invoice/src/main/resources/org/killbill/billing/invoice/dao/InvoiceItemSqlDao.sql.stg
@@ -85,4 +85,17 @@ getInvoiceItemsByParentInvoice() ::= <<
<AND_CHECK_TENANT("items.")>
<defaultOrderBy()>
;
->>
\ No newline at end of file
+>>
+
+getAccountCBA() ::= <<
+select coalesce(sum(ii.amount), 0) cba
+from invoice_items ii
+join invoices i on i.id = ii.invoice_id
+where i.status = 'COMMITTED'
+and ii.type = 'CBA_ADJ'
+and <accountRecordIdField("i.")> = :accountRecordId
+and <accountRecordIdField("ii.")> = :accountRecordId
+<AND_CHECK_TENANT("i.")>
+<AND_CHECK_TENANT("ii.")>
+;
+>>
diff --git a/invoice/src/test/java/org/killbill/billing/invoice/dao/MockInvoiceDao.java b/invoice/src/test/java/org/killbill/billing/invoice/dao/MockInvoiceDao.java
index 8ba0cc8..8ddb1f2 100644
--- a/invoice/src/test/java/org/killbill/billing/invoice/dao/MockInvoiceDao.java
+++ b/invoice/src/test/java/org/killbill/billing/invoice/dao/MockInvoiceDao.java
@@ -61,8 +61,8 @@ public class MockInvoiceDao extends MockEntityDaoBase<InvoiceModelDao, Invoice,
}
@Override
- public void createInvoice(final InvoiceModelDao invoice, final List<InvoiceItemModelDao> invoiceItems,
- final boolean isRealInvoice, final FutureAccountNotifications callbackDateTimePerSubscriptions, final InternalCallContext context) {
+ public void createInvoice(final InvoiceModelDao invoice,
+ final FutureAccountNotifications callbackDateTimePerSubscriptions, final InternalCallContext context) {
synchronized (monitor) {
storeInvoice(invoice, context);
}
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 1bf8600..1cb8bbf 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
@@ -1,7 +1,7 @@
/*
* Copyright 2010-2013 Ning, Inc.
- * Copyright 2014-2016 Groupon, Inc
- * Copyright 2014-2016 The Billing Project, LLC
+ * Copyright 2014-2017 Groupon, Inc
+ * Copyright 2014-2017 The Billing Project, LLC
*
* The Billing Project licenses this file to you under the Apache License, version 2.0
* (the "License"); you may not use this file except in compliance with the
@@ -45,8 +45,6 @@ import org.killbill.billing.catalog.api.PhaseType;
import org.killbill.billing.catalog.api.Plan;
import org.killbill.billing.catalog.api.PlanPhase;
import org.killbill.billing.entity.EntityPersistenceException;
-import org.killbill.billing.invoice.InvoiceDispatcher.FutureAccountNotifications;
-import org.killbill.billing.invoice.InvoiceDispatcher.FutureAccountNotifications.SubscriptionNotification;
import org.killbill.billing.invoice.InvoiceTestSuiteWithEmbeddedDB;
import org.killbill.billing.invoice.MockBillingEventSet;
import org.killbill.billing.invoice.api.Invoice;
@@ -106,7 +104,7 @@ public class TestInvoiceDao extends InvoiceTestSuiteWithEmbeddedDB {
public void testSimple() throws Exception {
final UUID accountId = account.getId();
final Invoice invoice = new DefaultInvoice(accountId, clock.getUTCToday(), clock.getUTCToday(), Currency.USD);
- invoiceUtil.createInvoice(invoice, true, context);
+ invoiceUtil.createInvoice(invoice, context);
final InvoiceModelDao retrievedInvoice = invoiceDao.getById(invoice.getId(), context);
invoiceUtil.checkInvoicesEqual(retrievedInvoice, invoice);
@@ -114,12 +112,12 @@ public class TestInvoiceDao extends InvoiceTestSuiteWithEmbeddedDB {
}
@Test(groups = "slow")
- public void testCreationAndRetrievalByAccount() {
+ public void testCreationAndRetrievalByAccount() throws EntityPersistenceException {
final UUID accountId = account.getId();
final Invoice invoice = new DefaultInvoice(accountId, clock.getUTCToday(), clock.getUTCToday(), Currency.USD);
final LocalDate invoiceDate = invoice.getInvoiceDate();
- invoiceUtil.createInvoice(invoice, true, context);
+ invoiceUtil.createInvoice(invoice, context);
final List<InvoiceModelDao> invoices = invoiceDao.getInvoicesByAccount(context);
assertNotNull(invoices);
@@ -133,7 +131,7 @@ public class TestInvoiceDao extends InvoiceTestSuiteWithEmbeddedDB {
}
@Test(groups = "slow")
- public void testInvoicePayment() throws InvoiceApiException {
+ public void testInvoicePayment() throws InvoiceApiException, EntityPersistenceException {
final UUID accountId = account.getId();
final Invoice invoice = new DefaultInvoice(accountId, clock.getUTCToday(), clock.getUTCToday(), Currency.USD);
final UUID invoiceId = invoice.getId();
@@ -145,7 +143,7 @@ public class TestInvoiceDao extends InvoiceTestSuiteWithEmbeddedDB {
new BigDecimal("21.00"), new BigDecimal("7.00"), Currency.USD);
invoice.addInvoiceItem(invoiceItem);
- invoiceUtil.createInvoice(invoice, true, context);
+ invoiceUtil.createInvoice(invoice, context);
final InvoiceModelDao savedInvoice = invoiceDao.getById(invoiceId, context);
assertNotNull(savedInvoice);
@@ -239,7 +237,7 @@ public class TestInvoiceDao extends InvoiceTestSuiteWithEmbeddedDB {
// Create invoice 1 (subscriptions 1-4)
final Invoice invoice1 = new DefaultInvoice(accountId, clock.getUTCToday(), targetDate, Currency.USD);
- invoiceUtil.createInvoice(invoice1, true, context);
+ invoiceUtil.createInvoice(invoice1, context);
final UUID invoiceId1 = invoice1.getId();
@@ -264,7 +262,7 @@ public class TestInvoiceDao extends InvoiceTestSuiteWithEmbeddedDB {
// Create invoice 2 (subscriptions 1-3)
final DefaultInvoice invoice2 = new DefaultInvoice(accountId, clock.getUTCToday(), targetDate, Currency.USD);
- invoiceUtil.createInvoice(invoice2, true, context);
+ invoiceUtil.createInvoice(invoice2, context);
final UUID invoiceId2 = invoice2.getId();
@@ -315,7 +313,7 @@ public class TestInvoiceDao extends InvoiceTestSuiteWithEmbeddedDB {
// Create invoice 1 (subscriptions 1-4)
final Invoice invoice1 = new DefaultInvoice(accountId, clock.getUTCToday(), targetDate, Currency.USD);
- invoiceUtil.createInvoice(invoice1, true, context);
+ invoiceUtil.createInvoice(invoice1, context);
final UUID invoiceId1 = invoice1.getId();
@@ -340,7 +338,7 @@ public class TestInvoiceDao extends InvoiceTestSuiteWithEmbeddedDB {
// create invoice 2 (subscriptions 1-3)
final DefaultInvoice invoice2 = new DefaultInvoice(accountId, clock.getUTCToday(), targetDate, Currency.USD);
- invoiceUtil.createInvoice(invoice2, true, context);
+ invoiceUtil.createInvoice(invoice2, context);
final UUID invoiceId2 = invoice2.getId();
@@ -390,7 +388,7 @@ public class TestInvoiceDao extends InvoiceTestSuiteWithEmbeddedDB {
// Create invoice 1 (subscriptions 1-4)
final Invoice invoice1 = new DefaultInvoice(accountId, clock.getUTCToday(), targetDate, Currency.USD);
- invoiceUtil.createInvoice(invoice1, true, context);
+ invoiceUtil.createInvoice(invoice1, context);
final UUID invoiceId1 = invoice1.getId();
@@ -431,7 +429,7 @@ public class TestInvoiceDao extends InvoiceTestSuiteWithEmbeddedDB {
// create invoice 2 (subscriptions 1-3)
final DefaultInvoice invoice2 = new DefaultInvoice(accountId, clock.getUTCToday(), targetDate, Currency.USD);
- invoiceUtil.createInvoice(invoice2, true, context);
+ invoiceUtil.createInvoice(invoice2, context);
final UUID invoiceId2 = invoice2.getId();
@@ -476,15 +474,15 @@ public class TestInvoiceDao extends InvoiceTestSuiteWithEmbeddedDB {
}
@Test(groups = "slow")
- public void testGetInvoicesForAccountAfterDate() {
+ public void testGetInvoicesForAccountAfterDate() throws EntityPersistenceException {
final UUID accountId = account.getId();
final LocalDate targetDate1 = new LocalDate(2011, 10, 6);
final Invoice invoice1 = new DefaultInvoice(accountId, clock.getUTCToday(), targetDate1, Currency.USD);
- invoiceUtil.createInvoice(invoice1, true, context);
+ invoiceUtil.createInvoice(invoice1, context);
final LocalDate targetDate2 = new LocalDate(2011, 12, 6);
final Invoice invoice2 = new DefaultInvoice(accountId, clock.getUTCToday(), targetDate2, Currency.USD);
- invoiceUtil.createInvoice(invoice2, true, context);
+ invoiceUtil.createInvoice(invoice2, context);
List<InvoiceModelDao> invoices;
invoices = invoiceDao.getInvoicesByAccount(new LocalDate(2011, 1, 1), context);
@@ -509,7 +507,7 @@ public class TestInvoiceDao extends InvoiceTestSuiteWithEmbeddedDB {
final UUID bundleId = UUID.randomUUID();
final LocalDate targetDate1 = new LocalDate(2011, 10, 6);
final Invoice invoice1 = new DefaultInvoice(accountId, clock.getUTCToday(), targetDate1, Currency.USD);
- invoiceUtil.createInvoice(invoice1, true, context);
+ invoiceUtil.createInvoice(invoice1, context);
final LocalDate startDate = new LocalDate(2011, 3, 1);
final LocalDate endDate = startDate.plusMonths(1);
@@ -539,7 +537,7 @@ public class TestInvoiceDao extends InvoiceTestSuiteWithEmbeddedDB {
final UUID bundleId = UUID.randomUUID();
final LocalDate targetDate1 = new LocalDate(2011, 10, 6);
final Invoice invoice1 = new DefaultInvoice(accountId, clock.getUTCToday(), targetDate1, Currency.USD);
- invoiceUtil.createInvoice(invoice1, true, context);
+ invoiceUtil.createInvoice(invoice1, context);
final LocalDate startDate = new LocalDate(2011, 3, 1);
final LocalDate endDate = startDate.plusMonths(1);
@@ -563,7 +561,7 @@ public class TestInvoiceDao extends InvoiceTestSuiteWithEmbeddedDB {
final UUID bundleId = UUID.randomUUID();
final LocalDate targetDate1 = new LocalDate(2011, 10, 6);
final Invoice invoice1 = new DefaultInvoice(accountId, clock.getUTCToday(), targetDate1, Currency.USD);
- invoiceUtil.createInvoice(invoice1, true, context);
+ invoiceUtil.createInvoice(invoice1, context);
final LocalDate startDate = new LocalDate(2011, 3, 1);
final LocalDate endDate = startDate.plusMonths(1);
@@ -588,7 +586,7 @@ public class TestInvoiceDao extends InvoiceTestSuiteWithEmbeddedDB {
final UUID accountId = account.getId();
final LocalDate targetDate1 = new LocalDate(2011, 10, 6);
final Invoice invoice1 = new DefaultInvoice(accountId, clock.getUTCToday(), targetDate1, Currency.USD);
- invoiceUtil.createInvoice(invoice1, true, context);
+ invoiceUtil.createInvoice(invoice1, 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, null, true);
@@ -609,7 +607,7 @@ public class TestInvoiceDao extends InvoiceTestSuiteWithEmbeddedDB {
final UUID bundleId = UUID.randomUUID();
final LocalDate targetDate1 = new LocalDate(2011, 10, 6);
final Invoice invoice1 = new DefaultInvoice(accountId, clock.getUTCToday(), targetDate1, Currency.USD);
- invoiceUtil.createInvoice(invoice1, true, context);
+ invoiceUtil.createInvoice(invoice1, context);
final LocalDate startDate = new LocalDate(2011, 3, 1);
final LocalDate endDate = startDate.plusMonths(1);
@@ -659,7 +657,7 @@ public class TestInvoiceDao extends InvoiceTestSuiteWithEmbeddedDB {
final LocalDate targetDate1 = new LocalDate(2011, 10, 6);
final Invoice invoice = new DefaultInvoice(accountId, clock.getUTCToday(), targetDate1, Currency.USD);
- invoiceUtil.createInvoice(invoice, true, context);
+ invoiceUtil.createInvoice(invoice, context);
final LocalDate startDate = new LocalDate(2011, 3, 1);
final LocalDate endDate = startDate.plusMonths(1);
@@ -747,7 +745,7 @@ public class TestInvoiceDao extends InvoiceTestSuiteWithEmbeddedDB {
final UUID bundleId = UUID.randomUUID();
final LocalDate targetDate1 = new LocalDate(2011, 10, 6);
final Invoice invoice1 = new DefaultInvoice(accountId, clock.getUTCToday(), targetDate1, Currency.USD);
- invoiceUtil.createInvoice(invoice1, true, context);
+ invoiceUtil.createInvoice(invoice1, context);
final LocalDate startDate = new LocalDate(2011, 3, 1);
final LocalDate endDate = startDate.plusMonths(1);
@@ -815,7 +813,7 @@ public class TestInvoiceDao extends InvoiceTestSuiteWithEmbeddedDB {
final UUID accountId = account.getId();
final UUID bundleId = UUID.randomUUID();
- createCredit(accountId, clock.getUTCToday(), new BigDecimal("20.0"));
+ final InvoiceItemModelDao credit = createCredit(accountId, clock.getUTCToday(), new BigDecimal("20.0"), true);
final String description = UUID.randomUUID().toString();
final InvoiceModelDao invoiceForExternalCharge = new InvoiceModelDao(accountId, clock.getUTCToday(), clock.getUTCToday(), Currency.USD, false);
@@ -823,8 +821,55 @@ public class TestInvoiceDao extends InvoiceTestSuiteWithEmbeddedDB {
invoiceForExternalCharge.addInvoiceItem(externalCharge);
final InvoiceItemModelDao charge = invoiceDao.createInvoices(ImmutableList.<InvoiceModelDao>of(invoiceForExternalCharge), context).get(0);
- final InvoiceModelDao newInvoice = invoiceDao.getById(charge.getInvoiceId(), context);
- final List<InvoiceItemModelDao> items = newInvoice.getInvoiceItems();
+ InvoiceModelDao newInvoice = invoiceDao.getById(charge.getInvoiceId(), context);
+ List<InvoiceItemModelDao> items = newInvoice.getInvoiceItems();
+ // No CBA consumed yet since the credit was created on a DRAFT invoice
+ assertEquals(items.size(), 1);
+ assertEquals(items.get(0).getType(), InvoiceItemType.EXTERNAL_CHARGE);
+ assertEquals(items.get(0).getDescription(), description);
+
+ invoiceDao.changeInvoiceStatus(credit.getInvoiceId(), InvoiceStatus.COMMITTED, context);
+
+ // CBA should have been consumed
+ newInvoice = invoiceDao.getById(charge.getInvoiceId(), context);
+ items = newInvoice.getInvoiceItems();
+ assertEquals(items.size(), 2);
+ for (final InvoiceItemModelDao cur : items) {
+ if (cur.getId().equals(charge.getId())) {
+ assertEquals(cur.getType(), InvoiceItemType.EXTERNAL_CHARGE);
+ assertEquals(cur.getDescription(), description);
+ } else {
+ assertEquals(cur.getType(), InvoiceItemType.CBA_ADJ);
+ assertTrue(cur.getAmount().compareTo(new BigDecimal("-15.00")) == 0);
+ }
+ }
+ }
+
+ @Test(groups = "slow")
+ public void testExternalChargeOnDRAFTInvoiceWithCBA() throws InvoiceApiException, EntityPersistenceException {
+ final UUID accountId = account.getId();
+ final UUID bundleId = UUID.randomUUID();
+
+ final InvoiceItemModelDao credit = createCredit(accountId, clock.getUTCToday(), new BigDecimal("20.0"), false);
+
+ final String description = UUID.randomUUID().toString();
+ final InvoiceModelDao draftInvoiceForExternalCharge = new InvoiceModelDao(accountId, clock.getUTCToday(), clock.getUTCToday(), Currency.USD, false, InvoiceStatus.DRAFT);
+ final InvoiceItemModelDao externalCharge = new InvoiceItemModelDao(new ExternalChargeInvoiceItem(draftInvoiceForExternalCharge.getId(), accountId, bundleId, description, clock.getUTCToday(), new BigDecimal("15.0"), Currency.USD));
+ draftInvoiceForExternalCharge.addInvoiceItem(externalCharge);
+ final InvoiceItemModelDao charge = invoiceDao.createInvoices(ImmutableList.<InvoiceModelDao>of(draftInvoiceForExternalCharge), context).get(0);
+
+ InvoiceModelDao newInvoice = invoiceDao.getById(charge.getInvoiceId(), context);
+ List<InvoiceItemModelDao> items = newInvoice.getInvoiceItems();
+ // No CBA consumed yet since the charge was created on a DRAFT invoice
+ assertEquals(items.size(), 1);
+ assertEquals(items.get(0).getType(), InvoiceItemType.EXTERNAL_CHARGE);
+ assertEquals(items.get(0).getDescription(), description);
+
+ invoiceDao.changeInvoiceStatus(charge.getInvoiceId(), InvoiceStatus.COMMITTED, context);
+
+ // CBA should have been consumed
+ newInvoice = invoiceDao.getById(charge.getInvoiceId(), context);
+ items = newInvoice.getInvoiceItems();
assertEquals(items.size(), 2);
for (final InvoiceItemModelDao cur : items) {
if (cur.getId().equals(charge.getId())) {
@@ -843,7 +888,7 @@ public class TestInvoiceDao extends InvoiceTestSuiteWithEmbeddedDB {
final UUID bundleId = UUID.randomUUID();
final LocalDate targetDate1 = new LocalDate(2011, 10, 6);
final Invoice invoice1 = new DefaultInvoice(accountId, clock.getUTCToday(), targetDate1, Currency.USD);
- invoiceUtil.createInvoice(invoice1, true, context);
+ invoiceUtil.createInvoice(invoice1, context);
final LocalDate startDate = new LocalDate(2011, 3, 1);
final LocalDate endDate = startDate.plusMonths(1);
@@ -906,7 +951,7 @@ public class TestInvoiceDao extends InvoiceTestSuiteWithEmbeddedDB {
// NEXT RECURRING on invoice 2
final Invoice invoice2 = new DefaultInvoice(accountId, clock.getUTCToday(), targetDate1.plusMonths(1), Currency.USD);
- invoiceUtil.createInvoice(invoice2, true, context);
+ invoiceUtil.createInvoice(invoice2, context);
final RecurringInvoiceItem nextItem = new RecurringInvoiceItem(invoice2.getId(), accountId, bundleId, UUID.randomUUID(), "test plan", "test bla", startDate.plusMonths(1),
endDate.plusMonths(1), rate2, rate2, Currency.USD);
@@ -926,13 +971,13 @@ public class TestInvoiceDao extends InvoiceTestSuiteWithEmbeddedDB {
}
@Test(groups = "slow")
- public void testAccountCredit() {
+ public void testAccountCredit() throws InvoiceApiException {
final UUID accountId = account.getId();
final LocalDate effectiveDate = new LocalDate(2011, 3, 1);
final BigDecimal creditAmount = new BigDecimal("5.0");
- createCredit(accountId, effectiveDate, creditAmount);
+ createCredit(accountId, effectiveDate, creditAmount, true);
final List<InvoiceModelDao> invoices = invoiceDao.getAllInvoicesByAccount(context);
assertEquals(invoices.size(), 1);
@@ -954,6 +999,12 @@ public class TestInvoiceDao extends InvoiceTestSuiteWithEmbeddedDB {
}
assertTrue(foundCredit);
assertTrue(foundCBA);
+
+ // No account CBA yet since the invoice is in DRAFT mode
+ assertEquals(invoiceDao.getAccountCBA(accountId, context).compareTo(BigDecimal.ZERO), 0);
+
+ invoiceDao.changeInvoiceStatus(invoice.getId(), InvoiceStatus.COMMITTED, context);
+ assertEquals(invoiceDao.getAccountCBA(accountId, context).compareTo(creditAmount), 0);
}
@Test(groups = "slow")
@@ -987,7 +1038,7 @@ public class TestInvoiceDao extends InvoiceTestSuiteWithEmbeddedDB {
// Create one invoice with a fixed invoice item
final LocalDate targetDate = new LocalDate(2011, 2, 15);
final Invoice invoice1 = new DefaultInvoice(accountId, clock.getUTCToday(), targetDate, Currency.USD);
- invoiceUtil.createInvoice(invoice1, true, context);
+ invoiceUtil.createInvoice(invoice1, context);
final LocalDate startDate = new LocalDate(2011, 3, 1);
@@ -1001,7 +1052,7 @@ public class TestInvoiceDao extends InvoiceTestSuiteWithEmbeddedDB {
// Create the credit item
final LocalDate effectiveDate = new LocalDate(2011, 3, 1);
- createCredit(accountId, invoice1.getId(), effectiveDate, creditAmount);
+ createCredit(accountId, invoice1.getId(), effectiveDate, creditAmount, false);
final List<InvoiceModelDao> invoices = invoiceDao.getAllInvoicesByAccount(context);
assertEquals(invoices.size(), 1);
@@ -1030,7 +1081,7 @@ public class TestInvoiceDao extends InvoiceTestSuiteWithEmbeddedDB {
final UUID bundleId = UUID.randomUUID();
final LocalDate targetDate1 = new LocalDate(2011, 10, 6);
final Invoice invoice1 = new DefaultInvoice(accountId, clock.getUTCToday(), targetDate1, Currency.USD);
- invoiceUtil.createInvoice(invoice1, true, context);
+ invoiceUtil.createInvoice(invoice1, context);
final LocalDate startDate = new LocalDate(2011, 3, 1);
final LocalDate endDate = startDate.plusMonths(1);
@@ -1059,7 +1110,7 @@ public class TestInvoiceDao extends InvoiceTestSuiteWithEmbeddedDB {
final LocalDate targetDate2 = new LocalDate(2011, 7, 1);
final Invoice invoice2 = new DefaultInvoice(accountId, clock.getUTCToday(), targetDate2, Currency.USD);
- invoiceUtil.createInvoice(invoice2, true, context);
+ invoiceUtil.createInvoice(invoice2, context);
final LocalDate startDate2 = new LocalDate(2011, 6, 1);
final LocalDate endDate2 = startDate2.plusMonths(3);
@@ -1085,7 +1136,7 @@ public class TestInvoiceDao extends InvoiceTestSuiteWithEmbeddedDB {
final UUID bundleId = UUID.randomUUID();
final LocalDate targetDate1 = new LocalDate(2011, 10, 6);
final Invoice invoice1 = new DefaultInvoice(accountId, clock.getUTCToday(), targetDate1, Currency.USD);
- invoiceUtil.createInvoice(invoice1, true, context);
+ invoiceUtil.createInvoice(invoice1, context);
final LocalDate startDate = new LocalDate(2011, 3, 1);
final LocalDate endDate = startDate.plusMonths(1);
@@ -1116,7 +1167,7 @@ public class TestInvoiceDao extends InvoiceTestSuiteWithEmbeddedDB {
assertEquals(allInvoicesByAccount.size(), 1);
// insert DRAFT invoice
- createCredit(accountId, new LocalDate(2011, 12, 31), BigDecimal.TEN);
+ createCredit(accountId, new LocalDate(2011, 12, 31), BigDecimal.TEN, true);
allInvoicesByAccount = invoiceDao.getInvoicesByAccount(new LocalDate(2011, 1, 1), context);
assertEquals(allInvoicesByAccount.size(), 2);
@@ -1134,7 +1185,7 @@ public class TestInvoiceDao extends InvoiceTestSuiteWithEmbeddedDB {
*
*/
@Test(groups = "slow")
- public void testInvoiceGenerationForImmediateChanges() throws InvoiceApiException, CatalogApiException {
+ public void testInvoiceGenerationForImmediateChanges() throws InvoiceApiException, CatalogApiException, EntityPersistenceException {
final UUID accountId = account.getId();
final List<Invoice> invoiceList = new ArrayList<Invoice>();
final LocalDate targetDate = new LocalDate(2011, 2, 16);
@@ -1181,8 +1232,8 @@ public class TestInvoiceDao extends InvoiceTestSuiteWithEmbeddedDB {
assertEquals(invoice2.getBalance(), KillBillMoney.of(FIVE, invoice2.getCurrency()));
invoiceList.add(invoice2);
- invoiceUtil.createInvoice(invoice1, true, context);
- invoiceUtil.createInvoice(invoice2, true, context);
+ invoiceUtil.createInvoice(invoice1, context);
+ invoiceUtil.createInvoice(invoice2, context);
final InvoiceModelDao savedInvoice1 = invoiceDao.getById(invoice1.getId(), context);
assertEquals(InvoiceModelDaoHelper.getBalance(savedInvoice1), KillBillMoney.of(TEN, savedInvoice1.getCurrency()));
@@ -1297,7 +1348,7 @@ public class TestInvoiceDao extends InvoiceTestSuiteWithEmbeddedDB {
}
@Test(groups = "slow")
- public void testMixedModeInvoicePersistence() throws InvoiceApiException, CatalogApiException {
+ public void testMixedModeInvoicePersistence() throws InvoiceApiException, CatalogApiException, EntityPersistenceException {
final Currency currency = Currency.USD;
final DefaultPrice zeroPrice = new DefaultPrice(BigDecimal.ZERO, Currency.USD);
final MockInternationalPrice fixedPrice = new MockInternationalPrice(zeroPrice);
@@ -1332,7 +1383,7 @@ public class TestInvoiceDao extends InvoiceTestSuiteWithEmbeddedDB {
assertEquals(invoice.getNumberOfItems(), 2);
assertEquals(invoice.getBalance().compareTo(cheapAmount), 0);
- invoiceUtil.createInvoice(invoice, true, context);
+ invoiceUtil.createInvoice(invoice, context);
final InvoiceModelDao savedInvoice = invoiceDao.getById(invoice.getId(), context);
assertNotNull(savedInvoice);
@@ -1341,7 +1392,7 @@ public class TestInvoiceDao extends InvoiceTestSuiteWithEmbeddedDB {
}
@Test(groups = "slow")
- public void testRefundedInvoiceWithInvoiceItemAdjustmentWithRepair() throws InvoiceApiException {
+ public void testRefundedInvoiceWithInvoiceItemAdjustmentWithRepair() throws InvoiceApiException, EntityPersistenceException {
final UUID accountId = account.getId();
final UUID subscriptionId = UUID.randomUUID();
final UUID bundleId = UUID.randomUUID();
@@ -1361,7 +1412,7 @@ public class TestInvoiceDao extends InvoiceTestSuiteWithEmbeddedDB {
recuringStartDate, recuringEndDate, new BigDecimal("239.00"), new BigDecimal("239.00"), Currency.USD);
invoice.addInvoiceItem(invoiceItem);
- invoiceUtil.createInvoice(invoice, true, context);
+ invoiceUtil.createInvoice(invoice, context);
((ClockMock) clock).addDays(1);
@@ -1400,7 +1451,7 @@ public class TestInvoiceDao extends InvoiceTestSuiteWithEmbeddedDB {
events.add(event1);
final InvoiceWithMetadata newInvoiceWithMetadata = generator.generateInvoice(account, events, invoices, targetDate, Currency.USD, context);
final Invoice newInvoice = newInvoiceWithMetadata.getInvoice();
- invoiceUtil.createInvoice(newInvoice, true, context);
+ invoiceUtil.createInvoice(newInvoice, context);
// VERIFY THAT WE STILL HAVE ONLY 2 ITEMS, MEANING THERE WERE NO REPAIR AND NO CBA GENERATED
final Invoice firstInvoice = new DefaultInvoice(invoiceDao.getById(invoiceId, context));
@@ -1409,7 +1460,7 @@ public class TestInvoiceDao extends InvoiceTestSuiteWithEmbeddedDB {
}
@Test(groups = "slow")
- public void testInvoiceNumber() throws InvoiceApiException {
+ public void testInvoiceNumber() throws InvoiceApiException, EntityPersistenceException {
final Currency currency = Currency.USD;
final DateTime targetDate1 = clock.getUTCNow().plusMonths(1);
final DateTime targetDate2 = clock.getUTCNow().plusMonths(2);
@@ -1437,7 +1488,7 @@ public class TestInvoiceDao extends InvoiceTestSuiteWithEmbeddedDB {
InvoiceWithMetadata invoiceWithMetadata1 = generator.generateInvoice(account, events, invoices, new LocalDate(targetDate1), Currency.USD, context);
Invoice invoice1 = invoiceWithMetadata1.getInvoice();
invoices.add(invoice1);
- invoiceUtil.createInvoice(invoice1, true, context);
+ invoiceUtil.createInvoice(invoice1, context);
invoice1 = new DefaultInvoice(invoiceDao.getById(invoice1.getId(), context));
assertNotNull(invoice1.getInvoiceNumber());
@@ -1448,7 +1499,7 @@ public class TestInvoiceDao extends InvoiceTestSuiteWithEmbeddedDB {
events.add(event2);
InvoiceWithMetadata invoiceWithMetadata2 = generator.generateInvoice(account, events, invoices, new LocalDate(targetDate2), Currency.USD, context);
Invoice invoice2 = invoiceWithMetadata2.getInvoice();
- invoiceUtil.createInvoice(invoice2, true, context);
+ invoiceUtil.createInvoice(invoice2, context);
invoice2 = new DefaultInvoice(invoiceDao.getById(invoice2.getId(), context));
assertNotNull(invoice2.getInvoiceNumber());
}
@@ -1473,7 +1524,7 @@ public class TestInvoiceDao extends InvoiceTestSuiteWithEmbeddedDB {
final CreditBalanceAdjInvoiceItem creditBalanceAdjInvoiceItem1 = new CreditBalanceAdjInvoiceItem(fixedItem1.getInvoiceId(), fixedItem1.getAccountId(),
fixedItem1.getStartDate(), fixedItem1.getAmount(),
fixedItem1.getCurrency());
- invoiceUtil.createInvoice(invoice1, true, context);
+ invoiceUtil.createInvoice(invoice1, context);
invoiceUtil.createInvoiceItem(fixedItem1, context);
invoiceUtil.createInvoiceItem(repairAdjInvoiceItem, context);
invoiceUtil.createInvoiceItem(creditBalanceAdjInvoiceItem1, context);
@@ -1511,7 +1562,7 @@ public class TestInvoiceDao extends InvoiceTestSuiteWithEmbeddedDB {
fixedItem1.getStartDate(), fixedItem1.getAmount(),
fixedItem1.getCurrency());
- invoiceUtil.createInvoice(invoice1, true, context);
+ invoiceUtil.createInvoice(invoice1, context);
invoiceUtil.createInvoiceItem(fixedItem1, context);
invoiceUtil.createInvoiceItem(repairAdjInvoiceItem, context);
invoiceUtil.createInvoiceItem(creditBalanceAdjInvoiceItem1, context);
@@ -1532,7 +1583,7 @@ public class TestInvoiceDao extends InvoiceTestSuiteWithEmbeddedDB {
final CreditBalanceAdjInvoiceItem creditBalanceAdjInvoiceItem2 = new CreditBalanceAdjInvoiceItem(fixedItem2.getInvoiceId(), fixedItem2.getAccountId(),
fixedItem2.getStartDate(), fixedItem2.getAmount().negate(),
fixedItem2.getCurrency());
- invoiceUtil.createInvoice(invoice2, true, context);
+ invoiceUtil.createInvoice(invoice2, context);
invoiceUtil.createInvoiceItem(fixedItem2, context);
invoiceUtil.createInvoiceItem(creditBalanceAdjInvoiceItem2, context);
@@ -1570,7 +1621,7 @@ public class TestInvoiceDao extends InvoiceTestSuiteWithEmbeddedDB {
final CreditBalanceAdjInvoiceItem creditBalanceAdjInvoiceItem1 = new CreditBalanceAdjInvoiceItem(fixedItem1.getInvoiceId(), fixedItem1.getAccountId(),
fixedItem1.getStartDate(), fixedItem1.getAmount(),
fixedItem1.getCurrency());
- invoiceUtil.createInvoice(invoice1, true, context);
+ invoiceUtil.createInvoice(invoice1, context);
invoiceUtil.createInvoiceItem(fixedItem1, context);
invoiceUtil.createInvoiceItem(repairAdjInvoiceItem, context);
invoiceUtil.createInvoiceItem(creditBalanceAdjInvoiceItem1, context);
@@ -1592,7 +1643,7 @@ public class TestInvoiceDao extends InvoiceTestSuiteWithEmbeddedDB {
final CreditBalanceAdjInvoiceItem creditBalanceAdjInvoiceItem2 = new CreditBalanceAdjInvoiceItem(fixedItem2.getInvoiceId(), fixedItem2.getAccountId(),
fixedItem2.getStartDate(), fixedItem2.getAmount().negate(),
fixedItem2.getCurrency());
- invoiceUtil.createInvoice(invoice2, true, context);
+ invoiceUtil.createInvoice(invoice2, context);
invoiceUtil.createInvoiceItem(fixedItem2, context);
invoiceUtil.createInvoiceItem(creditBalanceAdjInvoiceItem2, context);
@@ -1606,7 +1657,7 @@ public class TestInvoiceDao extends InvoiceTestSuiteWithEmbeddedDB {
final CreditBalanceAdjInvoiceItem creditBalanceAdjInvoiceItem3 = new CreditBalanceAdjInvoiceItem(fixedItem3.getInvoiceId(), fixedItem3.getAccountId(),
fixedItem3.getStartDate(), fixedItem3.getAmount().negate(),
fixedItem3.getCurrency());
- invoiceUtil.createInvoice(invoice3, true, context);
+ invoiceUtil.createInvoice(invoice3, context);
invoiceUtil.createInvoiceItem(fixedItem3, context);
invoiceUtil.createInvoiceItem(creditBalanceAdjInvoiceItem3, context);
@@ -1641,7 +1692,7 @@ public class TestInvoiceDao extends InvoiceTestSuiteWithEmbeddedDB {
final CreditBalanceAdjInvoiceItem creditBalanceAdjInvoiceItem1 = new CreditBalanceAdjInvoiceItem(invoice1.getId(), invoice1.getAccountId(),
invoice1.getInvoiceDate(), repairAdjInvoiceItem.getAmount().negate(),
invoice1.getCurrency());
- invoiceUtil.createInvoice(invoice1, true, context);
+ invoiceUtil.createInvoice(invoice1, context);
invoiceUtil.createInvoiceItem(repairAdjInvoiceItem, context);
invoiceUtil.createInvoiceItem(creditBalanceAdjInvoiceItem1, context);
@@ -1667,7 +1718,7 @@ public class TestInvoiceDao extends InvoiceTestSuiteWithEmbeddedDB {
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);
+ invoiceUtil.createInvoice(invoice, context);
final UUID bundleId = UUID.randomUUID();
final UUID subscriptionId = UUID.randomUUID();
@@ -1696,14 +1747,14 @@ public class TestInvoiceDao extends InvoiceTestSuiteWithEmbeddedDB {
}
- private void createCredit(final UUID accountId, final LocalDate effectiveDate, final BigDecimal creditAmount) {
- createCredit(accountId, null, effectiveDate, creditAmount);
+ private InvoiceItemModelDao createCredit(final UUID accountId, final LocalDate effectiveDate, final BigDecimal creditAmount, final boolean draft) {
+ return createCredit(accountId, null, effectiveDate, creditAmount, draft);
}
- private void createCredit(final UUID accountId, @Nullable final UUID invoiceId, final LocalDate effectiveDate, final BigDecimal creditAmount) {
+ private InvoiceItemModelDao createCredit(final UUID accountId, @Nullable final UUID invoiceId, final LocalDate effectiveDate, final BigDecimal creditAmount, final boolean draft) {
final InvoiceModelDao invoiceModelDao;
if (invoiceId == null) {
- invoiceModelDao = new InvoiceModelDao(accountId, effectiveDate, effectiveDate, Currency.USD, false, InvoiceStatus.DRAFT);
+ invoiceModelDao = new InvoiceModelDao(accountId, effectiveDate, effectiveDate, Currency.USD, false, draft ? InvoiceStatus.DRAFT : InvoiceStatus.COMMITTED);
} else {
invoiceModelDao = invoiceDao.getById(invoiceId, context);
}
@@ -1717,7 +1768,7 @@ public class TestInvoiceDao extends InvoiceTestSuiteWithEmbeddedDB {
creditAmount.negate(),
invoiceModelDao.getCurrency());
invoiceModelDao.addInvoiceItem(new InvoiceItemModelDao(invoiceItem));
- invoiceDao.createInvoices(ImmutableList.<InvoiceModelDao>of(invoiceModelDao), context);
+ return invoiceDao.createInvoices(ImmutableList.<InvoiceModelDao>of(invoiceModelDao), context).get(0);
}
@Test(groups = "slow")
@@ -1749,9 +1800,7 @@ public class TestInvoiceDao extends InvoiceTestSuiteWithEmbeddedDB {
InvoiceItem parentInvoiceItem = new ParentInvoiceItem(UUID.randomUUID(), today, parentInvoice.getId(), parentAccountId, childAccountId, BigDecimal.TEN, account.getCurrency(), "");
parentInvoice.addInvoiceItem(new InvoiceItemModelDao(parentInvoiceItem));
- // build account date time zone
- final FutureAccountNotifications futureAccountNotifications = new FutureAccountNotifications(ImmutableMap.<UUID, List<SubscriptionNotification>>of());
- invoiceDao.createInvoice(parentInvoice, parentInvoice.getInvoiceItems(), true, futureAccountNotifications, context);
+ invoiceDao.createInvoices(ImmutableList.<InvoiceModelDao>of(parentInvoice), context);
final InvoiceModelDao parentDraftInvoice = invoiceDao.getParentDraftInvoice(parentAccountId, context);
@@ -1762,7 +1811,7 @@ public class TestInvoiceDao extends InvoiceTestSuiteWithEmbeddedDB {
}
@Test(groups = "slow")
- public void testRetrieveInvoiceItemsByParentInvoice() throws InvoiceApiException {
+ public void testRetrieveInvoiceItemsByParentInvoice() throws InvoiceApiException, EntityPersistenceException {
final UUID childAccountId = account.getId();
final Invoice childInvoice = new DefaultInvoice(childAccountId, clock.getUTCToday(), clock.getUTCToday(), Currency.USD);
final UUID invoiceId = childInvoice.getId();
@@ -1776,7 +1825,7 @@ public class TestInvoiceDao extends InvoiceTestSuiteWithEmbeddedDB {
childInvoice.addInvoiceItem(invoiceItem);
childInvoice.addInvoiceItem(invoiceAdj);
- invoiceUtil.createInvoice(childInvoice, true, context);
+ invoiceUtil.createInvoice(childInvoice, context);
final UUID parentInvoiceId = UUID.randomUUID();
diff --git a/invoice/src/test/java/org/killbill/billing/invoice/dao/TestInvoiceItemDao.java b/invoice/src/test/java/org/killbill/billing/invoice/dao/TestInvoiceItemDao.java
index 20c682e..d5e3631 100644
--- a/invoice/src/test/java/org/killbill/billing/invoice/dao/TestInvoiceItemDao.java
+++ b/invoice/src/test/java/org/killbill/billing/invoice/dao/TestInvoiceItemDao.java
@@ -134,7 +134,7 @@ public class TestInvoiceItemDao extends InvoiceTestSuiteWithEmbeddedDB {
final LocalDate targetDate = new LocalDate(2011, 5, 23);
final DefaultInvoice invoice = new DefaultInvoice(accountId, clock.getUTCToday(), targetDate, Currency.USD);
- invoiceUtil.createInvoice(invoice, true, context);
+ invoiceUtil.createInvoice(invoice, context);
final UUID invoiceId = invoice.getId();
final LocalDate startDate = new LocalDate(2011, 3, 1);
diff --git a/invoice/src/test/java/org/killbill/billing/invoice/generator/TestInvoiceWithMetadata.java b/invoice/src/test/java/org/killbill/billing/invoice/generator/TestInvoiceWithMetadata.java
index 0bf40e0..afb5c54 100644
--- a/invoice/src/test/java/org/killbill/billing/invoice/generator/TestInvoiceWithMetadata.java
+++ b/invoice/src/test/java/org/killbill/billing/invoice/generator/TestInvoiceWithMetadata.java
@@ -72,7 +72,7 @@ public class TestInvoiceWithMetadata extends InvoiceTestSuiteNoDB {
final LocalDate invoiceDate = new LocalDate(2016, 11, 15);
- final Invoice originalInvoice = new DefaultInvoice(account.getId(), invoiceDate, account.getCurrency());
+ final DefaultInvoice originalInvoice = new DefaultInvoice(account.getId(), invoiceDate, account.getCurrency());
final Plan plan = new MockPlan("my-plan");
final MockInternationalPrice price = new MockInternationalPrice(new DefaultPrice(BigDecimal.TEN, account.getCurrency()));
@@ -134,4 +134,4 @@ public class TestInvoiceWithMetadata extends InvoiceTestSuiteNoDB {
Assert.assertEquals(futureNotificationDates.getNextRecurringDate().compareTo(invoiceDate.plusMonths(1)), 0 );
}
-}
\ No newline at end of file
+}
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 d526635..fc5f88b 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
@@ -106,7 +106,9 @@ public class InvoiceTestUtils {
}
Mockito.when(invoice.getInvoiceItems()).thenReturn(invoiceItems);
- invoiceDao.createInvoice(new InvoiceModelDao(invoice), invoiceModelItems, true, new FutureAccountNotifications(ImmutableMap.<UUID, List<SubscriptionNotification>>of()), internalCallContext);
+ final InvoiceModelDao invoiceModelDao = new InvoiceModelDao(invoice);
+ invoiceModelDao.addInvoiceItems(invoiceModelItems);
+ invoiceDao.createInvoices(ImmutableList.<InvoiceModelDao>of(invoiceModelDao), internalCallContext);
return invoice;
}
diff --git a/invoice/src/test/java/org/killbill/billing/invoice/TestInvoiceDispatcher.java b/invoice/src/test/java/org/killbill/billing/invoice/TestInvoiceDispatcher.java
index a83301d..5a86cc3 100644
--- a/invoice/src/test/java/org/killbill/billing/invoice/TestInvoiceDispatcher.java
+++ b/invoice/src/test/java/org/killbill/billing/invoice/TestInvoiceDispatcher.java
@@ -189,11 +189,10 @@ public class TestInvoiceDispatcher extends InvoiceTestSuiteWithEmbeddedDB {
BigDecimal.ONE,
currency,
null);
- invoiceDao.createInvoice(invoiceModelDao,
- ImmutableList.<InvoiceItemModelDao>of(invoiceItemModelDao1, invoiceItemModelDao2),
- true,
- new FutureAccountNotifications(ImmutableMap.<UUID, List<SubscriptionNotification>>of()),
- context);
+
+ invoiceModelDao.addInvoiceItem(invoiceItemModelDao1);
+ invoiceModelDao.addInvoiceItem(invoiceItemModelDao2);
+ invoiceDao.createInvoices(ImmutableList.<InvoiceModelDao>of(invoiceModelDao), context);
try {
dispatcher.processAccountFromNotificationOrBusEvent(accountId, target, new DryRunFutureDateArguments(), context);
diff --git a/invoice/src/test/java/org/killbill/billing/invoice/TestInvoiceHelper.java b/invoice/src/test/java/org/killbill/billing/invoice/TestInvoiceHelper.java
index 38dd1af..7708eb6 100644
--- a/invoice/src/test/java/org/killbill/billing/invoice/TestInvoiceHelper.java
+++ b/invoice/src/test/java/org/killbill/billing/invoice/TestInvoiceHelper.java
@@ -68,6 +68,7 @@ import org.killbill.billing.invoice.dao.InvoiceModelDao;
import org.killbill.billing.invoice.dao.InvoiceModelDaoHelper;
import org.killbill.billing.invoice.dao.InvoicePaymentModelDao;
import org.killbill.billing.invoice.dao.InvoicePaymentSqlDao;
+import org.killbill.billing.invoice.dao.InvoiceSqlDao;
import org.killbill.billing.invoice.generator.InvoiceGenerator;
import org.killbill.billing.invoice.notification.NullInvoiceNotifier;
import org.killbill.billing.junction.BillingEvent;
@@ -168,6 +169,7 @@ public class TestInvoiceHelper {
// Low level SqlDao used by the tests to directly insert rows
private final InvoicePaymentSqlDao invoicePaymentSqlDao;
private final InvoiceItemSqlDao invoiceItemSqlDao;
+ private final InvoiceSqlDao invoiceSqlDao;
@Inject
@@ -190,6 +192,7 @@ public class TestInvoiceHelper {
this.parkedAccountsManager = parkedAccountsManager;
this.internalCallContext = internalCallContext;
this.internalCallContextFactory = internalCallContextFactory;
+ this.invoiceSqlDao = dbi.onDemand(InvoiceSqlDao.class);
this.invoiceItemSqlDao = dbi.onDemand(InvoiceItemSqlDao.class);
this.invoicePaymentSqlDao = dbi.onDemand(InvoicePaymentSqlDao.class);
this.invoiceConfig = invoiceConfig;
@@ -290,7 +293,7 @@ public class TestInvoiceHelper {
return invoiceItemSqlDao.getInvoiceItemsByInvoice(invoiceId.toString(), internalCallContext);
}
- public void createInvoice(final Invoice invoice, final boolean isRealInvoiceWithItems, final InternalCallContext internalCallContext) {
+ public void createInvoice(final Invoice invoice, final InternalCallContext internalCallContext) throws EntityPersistenceException {
final InvoiceModelDao invoiceModelDao = new InvoiceModelDao(invoice);
final List<InvoiceItemModelDao> invoiceItemModelDaos = ImmutableList.<InvoiceItemModelDao>copyOf(Collections2.transform(invoice.getInvoiceItems(),
new Function<InvoiceItem, InvoiceItemModelDao>() {
@@ -299,17 +302,11 @@ public class TestInvoiceHelper {
return new InvoiceItemModelDao(input);
}
}));
- // Not really needed, there shouldn't be any payment at this stage
- final List<InvoicePaymentModelDao> invoicePaymentModelDaos = ImmutableList.<InvoicePaymentModelDao>copyOf(Collections2.transform(invoice.getPayments(),
- new Function<InvoicePayment, InvoicePaymentModelDao>() {
- @Override
- public InvoicePaymentModelDao apply(final InvoicePayment input) {
- return new InvoicePaymentModelDao(input);
- }
- }));
-
- // The test does not use the invoice callback notifier hence the empty map
- invoiceDao.createInvoice(invoiceModelDao, invoiceItemModelDaos, isRealInvoiceWithItems, new FutureAccountNotifications(ImmutableMap.<UUID, List<SubscriptionNotification>>of()), internalCallContext);
+ invoiceSqlDao.create(invoiceModelDao, internalCallContext);
+
+ for (final InvoiceItem invoiceItem : invoice.getInvoiceItems()) {
+ createInvoiceItem(invoiceItem, internalCallContext);
+ }
}
public void createPayment(final InvoicePayment invoicePayment, final InternalCallContext internalCallContext) {
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/SubscriptionJson.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/SubscriptionJson.java
index b3f9a9e..f65431f 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/SubscriptionJson.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/SubscriptionJson.java
@@ -397,7 +397,7 @@ public class SubscriptionJson extends JsonBase {
String currentPhaseName = null;
String currentPlanName = null;
- for (final SubscriptionEvent subscriptionEvent : subscription.getSubscriptionEvents()) {
+ for (final SubscriptionEvent subscriptionEvent : subscriptionEvents) {
this.events.add(new EventSubscriptionJson(subscriptionEvent, accountAuditLogs));
if (currency != null) {
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/SubscriptionResource.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/SubscriptionResource.java
index fa205c8..2d9aff1 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/SubscriptionResource.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/SubscriptionResource.java
@@ -276,7 +276,7 @@ public class SubscriptionResource extends JaxRsResourceBase {
final int baseSubscriptionsSize = Iterables.size(Iterables.filter(entitlements, new Predicate<SubscriptionJson>() {
@Override
public boolean apply(final SubscriptionJson subscription) {
- return subscription.getProductCategory().equals(ProductCategory.BASE.toString());
+ return ProductCategory.BASE.toString().equals(subscription.getProductCategory());
}
}));
verifyNumberOfElements(baseSubscriptionsSize, 1, "Only one BASE product is allowed.");
@@ -284,7 +284,7 @@ public class SubscriptionResource extends JaxRsResourceBase {
final int addOnSubscriptionsSize = Iterables.size(Iterables.filter(entitlements, new Predicate<SubscriptionJson>() {
@Override
public boolean apply(final SubscriptionJson subscription) {
- return subscription.getProductCategory().equals(ProductCategory.ADD_ON.toString());
+ return ProductCategory.ADD_ON.toString().equals(subscription.getProductCategory());
}
}));
verifyNumberOfElements(addOnSubscriptionsSize, entitlements.size() - 1, "It should be " + (entitlements.size() - 1) + " ADD_ON products.");
@@ -390,7 +390,7 @@ public class SubscriptionResource extends JaxRsResourceBase {
final int addOnSubscriptionsSize = Iterables.size(Iterables.filter(bulkBaseEntitlementWithAddOns.getBaseEntitlementAndAddOns(), new Predicate<SubscriptionJson>() {
@Override
public boolean apply(final SubscriptionJson subscription) {
- return subscription.getProductCategory().equals(ProductCategory.ADD_ON.toString());
+ return ProductCategory.ADD_ON.toString().equals(subscription.getProductCategory());
}
}));
verifyNumberOfElements(addOnSubscriptionsSize, bulkBaseEntitlementWithAddOns.getBaseEntitlementAndAddOns().size() - 1, "It should be " + (bulkBaseEntitlementWithAddOns.getBaseEntitlementAndAddOns().size() - 1) + " ADD_ON products.");
diff --git a/junction/src/main/java/org/killbill/billing/junction/plumbing/billing/BlockingCalculator.java b/junction/src/main/java/org/killbill/billing/junction/plumbing/billing/BlockingCalculator.java
index a03af67..cf7507e 100644
--- a/junction/src/main/java/org/killbill/billing/junction/plumbing/billing/BlockingCalculator.java
+++ b/junction/src/main/java/org/killbill/billing/junction/plumbing/billing/BlockingCalculator.java
@@ -131,7 +131,7 @@ public class BlockingCalculator {
}
final List<BlockingState> subscriptionBlockingEvents = perSubscriptionBlockingEvents.get(subscription.getId()) != null ? perSubscriptionBlockingEvents.get(subscription.getId()) : ImmutableList.<BlockingState>of();
- final List<BlockingState> aggregateSubscriptionBlockingEvents = getAggregateBlockingEventsPerSubscription(subscriptionBlockingEvents, bundleBlockingEvents, accountBlockingEvents);
+ final List<BlockingState> aggregateSubscriptionBlockingEvents = getAggregateBlockingEventsPerSubscription(subscription.getEndDate(), subscriptionBlockingEvents, bundleBlockingEvents, accountBlockingEvents);
final List<DisabledDuration> accountBlockingDurations = createBlockingDurations(aggregateSubscriptionBlockingEvents);
billingEventsToAdd.addAll(createNewEvents(accountBlockingDurations, billingEvents, subscription, context));
@@ -150,9 +150,16 @@ public class BlockingCalculator {
return !(billingEventsToAdd.isEmpty() && billingEventsToRemove.isEmpty());
}
- final List<BlockingState> getAggregateBlockingEventsPerSubscription(final Iterable<BlockingState> subscriptionBlockingEvents, final Iterable<BlockingState> bundleBlockingEvents, final Iterable<BlockingState> accountBlockingEvents) {
+ final List<BlockingState> getAggregateBlockingEventsPerSubscription(@Nullable final DateTime subscriptionEndDate, final Iterable<BlockingState> subscriptionBlockingEvents, final Iterable<BlockingState> bundleBlockingEvents, final Iterable<BlockingState> accountBlockingEvents) {
final Iterable<BlockingState> tmp = Iterables.concat(subscriptionBlockingEvents, bundleBlockingEvents, accountBlockingEvents);
- final List<BlockingState> result = Lists.newArrayList(tmp);
+ final Iterable<BlockingState> allEventsPriorToCancelDate = Iterables.filter(tmp,
+ new Predicate<BlockingState>() {
+ @Override
+ public boolean apply(final BlockingState input) {
+ return subscriptionEndDate == null || input.getEffectiveDate().compareTo(subscriptionEndDate) <= 0;
+ }
+ });
+ final List<BlockingState> result = Lists.newArrayList(allEventsPriorToCancelDate);
Collections.sort(result);
return result;
}
diff --git a/overdue/src/main/java/org/killbill/billing/overdue/applicator/formatters/DefaultBillingStateFormatter.java b/overdue/src/main/java/org/killbill/billing/overdue/applicator/formatters/DefaultBillingStateFormatter.java
index a71170e..76bf27a 100644
--- a/overdue/src/main/java/org/killbill/billing/overdue/applicator/formatters/DefaultBillingStateFormatter.java
+++ b/overdue/src/main/java/org/killbill/billing/overdue/applicator/formatters/DefaultBillingStateFormatter.java
@@ -20,7 +20,7 @@ import java.math.BigDecimal;
import org.killbill.billing.overdue.config.api.BillingState;
-import com.google.common.base.Objects;
+import com.google.common.base.MoreObjects;
import static org.killbill.billing.util.DefaultAmountFormatter.round;
@@ -32,6 +32,6 @@ public class DefaultBillingStateFormatter extends BillingStateFormatter {
@Override
public String getFormattedBalanceOfUnpaidInvoices() {
- return round(Objects.firstNonNull(getBalanceOfUnpaidInvoices(), BigDecimal.ZERO)).toString();
+ return round(MoreObjects.firstNonNull(getBalanceOfUnpaidInvoices(), BigDecimal.ZERO)).toString();
}
}
diff --git a/payment/src/main/java/org/killbill/billing/payment/api/DefaultPaymentApi.java b/payment/src/main/java/org/killbill/billing/payment/api/DefaultPaymentApi.java
index d52b6da..6174f20 100644
--- a/payment/src/main/java/org/killbill/billing/payment/api/DefaultPaymentApi.java
+++ b/payment/src/main/java/org/killbill/billing/payment/api/DefaultPaymentApi.java
@@ -90,7 +90,7 @@ public class DefaultPaymentApi extends DefaultApiBase implements PaymentApi {
final InternalCallContext internalCallContext = internalCallContextFactory.createInternalCallContext(account.getId(), callContext);
payment = paymentProcessor.createAuthorization(IS_API_PAYMENT, NULL_ATTEMPT_ID, account, paymentMethodId, paymentId, amount, currency, paymentExternalKey, paymentTransactionExternalKey,
- SHOULD_LOCK_ACCOUNT, properties, callContext, internalCallContext);
+ null, null, SHOULD_LOCK_ACCOUNT, properties, callContext, internalCallContext);
paymentTransaction = findPaymentTransaction(payment, paymentTransactionExternalKey);
@@ -187,7 +187,7 @@ public class DefaultPaymentApi extends DefaultApiBase implements PaymentApi {
final InternalCallContext internalCallContext = internalCallContextFactory.createInternalCallContext(account.getId(), callContext);
payment = paymentProcessor.createCapture(IS_API_PAYMENT, NULL_ATTEMPT_ID, account, paymentId, amount, currency, paymentTransactionExternalKey,
- SHOULD_LOCK_ACCOUNT, properties, callContext, internalCallContext);
+ null, SHOULD_LOCK_ACCOUNT, properties, callContext, internalCallContext);
paymentTransaction = findPaymentTransaction(payment, paymentTransactionExternalKey);
@@ -283,7 +283,7 @@ public class DefaultPaymentApi extends DefaultApiBase implements PaymentApi {
final InternalCallContext internalCallContext = internalCallContextFactory.createInternalCallContext(account.getId(), callContext);
payment = paymentProcessor.createPurchase(IS_API_PAYMENT, NULL_ATTEMPT_ID, account, paymentMethodId, paymentId, amount, currency, paymentExternalKey, paymentTransactionExternalKey,
- SHOULD_LOCK_ACCOUNT, properties, callContext, internalCallContext);
+ null, null, SHOULD_LOCK_ACCOUNT, properties, callContext, internalCallContext);
paymentTransaction = findPaymentTransaction(payment, paymentTransactionExternalKey);
@@ -388,7 +388,7 @@ public class DefaultPaymentApi extends DefaultApiBase implements PaymentApi {
final InternalCallContext internalCallContext = internalCallContextFactory.createInternalCallContext(account.getId(), callContext);
payment = paymentProcessor.createVoid(IS_API_PAYMENT, NULL_ATTEMPT_ID, account, paymentId, paymentTransactionExternalKey,
- SHOULD_LOCK_ACCOUNT, properties, callContext, internalCallContext);
+ null, SHOULD_LOCK_ACCOUNT, properties, callContext, internalCallContext);
paymentTransaction = findPaymentTransaction(payment, paymentTransactionExternalKey);
@@ -480,7 +480,7 @@ public class DefaultPaymentApi extends DefaultApiBase implements PaymentApi {
final InternalCallContext internalCallContext = internalCallContextFactory.createInternalCallContext(account.getId(), callContext);
payment = paymentProcessor.createRefund(IS_API_PAYMENT, NULL_ATTEMPT_ID, account, paymentId, amount, currency, paymentTransactionExternalKey,
- SHOULD_LOCK_ACCOUNT, properties, callContext, internalCallContext);
+ null, SHOULD_LOCK_ACCOUNT, properties, callContext, internalCallContext);
paymentTransaction = findPaymentTransaction(payment, paymentTransactionExternalKey);
@@ -581,7 +581,7 @@ public class DefaultPaymentApi extends DefaultApiBase implements PaymentApi {
paymentMethodProcessor.createOrGetExternalPaymentMethod(UUIDs.randomUUID().toString(), account, properties, callContext, internalCallContext);
payment = paymentProcessor.createCredit(IS_API_PAYMENT, NULL_ATTEMPT_ID, account, nonNullPaymentMethodId, paymentId, amount, currency, paymentExternalKey, paymentTransactionExternalKey,
- SHOULD_LOCK_ACCOUNT, properties, callContext, internalCallContext);
+ null, null, SHOULD_LOCK_ACCOUNT, properties, callContext, internalCallContext);
paymentTransaction = findPaymentTransaction(payment, paymentTransactionExternalKey);
@@ -784,8 +784,8 @@ public class DefaultPaymentApi extends DefaultApiBase implements PaymentApi {
logEnterAPICall(log, transactionType, account, null, paymentId, null, amount, currency, null, paymentTransactionExternalKey, null, null);
final InternalCallContext internalCallContext = internalCallContextFactory.createInternalCallContext(account.getId(), callContext);
- payment = paymentProcessor.createChargeback(IS_API_PAYMENT, NULL_ATTEMPT_ID, account, paymentId, paymentTransactionExternalKey, amount, currency, true,
- callContext, internalCallContext);
+ payment = paymentProcessor.createChargeback(IS_API_PAYMENT, NULL_ATTEMPT_ID, account, paymentId, paymentTransactionExternalKey, amount, currency, null, true,
+ callContext, internalCallContext);
paymentTransaction = findPaymentTransaction(payment, paymentTransactionExternalKey);
@@ -870,7 +870,7 @@ public class DefaultPaymentApi extends DefaultApiBase implements PaymentApi {
logEnterAPICall(log, transactionType, account, null, paymentId, null, null, null, null, paymentTransactionExternalKey, null, null);
final InternalCallContext internalCallContext = internalCallContextFactory.createInternalCallContext(account.getId(), callContext);
- payment = paymentProcessor.createChargebackReversal(IS_API_PAYMENT, NULL_ATTEMPT_ID, account, paymentId, paymentTransactionExternalKey, null, null, true, callContext, internalCallContext);
+ payment = paymentProcessor.createChargebackReversal(IS_API_PAYMENT, NULL_ATTEMPT_ID, account, paymentId, paymentTransactionExternalKey, null, null, null, true, callContext, internalCallContext);
paymentTransaction = findPaymentTransaction(payment, paymentTransactionExternalKey);
diff --git a/payment/src/main/java/org/killbill/billing/payment/bus/PaymentBusEventHandler.java b/payment/src/main/java/org/killbill/billing/payment/bus/PaymentBusEventHandler.java
index 58f91ca..30f5c13 100644
--- a/payment/src/main/java/org/killbill/billing/payment/bus/PaymentBusEventHandler.java
+++ b/payment/src/main/java/org/killbill/billing/payment/bus/PaymentBusEventHandler.java
@@ -103,9 +103,6 @@ public class PaymentBusEventHandler {
final List<String> paymentControlPluginNames = paymentConfig.getPaymentControlPluginNames(internalContext) != null ? new LinkedList<String>(paymentConfig.getPaymentControlPluginNames(internalContext)) : new LinkedList<String>();
paymentControlPluginNames.add(InvoicePaymentControlPluginApi.PLUGIN_NAME);
- final String paymentExternalKey = UUIDs.randomUUID().toString();
- final String paymentTransactionExternalKey = UUIDs.randomUUID().toString();
-
final String transactionType = TransactionType.PURCHASE.name();
Account account = null;
Payment payment = null;
@@ -121,20 +118,14 @@ public class PaymentBusEventHandler {
null,
amountToBePaid,
account.getCurrency(),
- paymentExternalKey,
- paymentTransactionExternalKey,
+ null,
+ null,
null,
paymentControlPluginNames);
- payment = pluginControlPaymentProcessor.createPurchase(false, account, account.getPaymentMethodId(), null, amountToBePaid, account.getCurrency(), paymentExternalKey, paymentTransactionExternalKey, properties, paymentControlPluginNames, callContext, internalContext);
+ payment = pluginControlPaymentProcessor.createPurchase(false, account, account.getPaymentMethodId(), null, amountToBePaid, account.getCurrency(), null, null, properties, paymentControlPluginNames, callContext, internalContext);
- paymentTransaction = Iterables.<PaymentTransaction>find(Lists.<PaymentTransaction>reverse(payment.getTransactions()),
- new Predicate<PaymentTransaction>() {
- @Override
- public boolean apply(final PaymentTransaction input) {
- return paymentTransactionExternalKey.equals(input.getExternalKey());
- }
- });
+ paymentTransaction = payment.getTransactions().get(payment.getTransactions().size() - 1);
} catch (final AccountApiException e) {
log.warn("Failed to process invoice payment", e);
} catch (final PaymentApiException e) {
diff --git a/payment/src/main/java/org/killbill/billing/payment/core/janitor/CompletionTaskBase.java b/payment/src/main/java/org/killbill/billing/payment/core/janitor/CompletionTaskBase.java
index 71fee5f..f29b8b6 100644
--- a/payment/src/main/java/org/killbill/billing/payment/core/janitor/CompletionTaskBase.java
+++ b/payment/src/main/java/org/killbill/billing/payment/core/janitor/CompletionTaskBase.java
@@ -1,6 +1,6 @@
/*
- * Copyright 2014-2016 Groupon, Inc
- * Copyright 2014-2016 The Billing Project, LLC
+ * Copyright 2014-2017 Groupon, Inc
+ * Copyright 2014-2017 The Billing Project, LLC
*
* The Billing Project licenses this file to you under the Apache License, version 2.0
* (the "License"); you may not use this file except in compliance with the
@@ -25,11 +25,9 @@ import org.killbill.billing.account.api.ImmutableAccountData;
import org.killbill.billing.callcontext.DefaultCallContext;
import org.killbill.billing.callcontext.InternalTenantContext;
import org.killbill.billing.events.PaymentInternalEvent;
-import org.killbill.billing.osgi.api.OSGIServiceRegistration;
import org.killbill.billing.payment.core.sm.PaymentControlStateMachineHelper;
import org.killbill.billing.payment.core.sm.PaymentStateMachineHelper;
import org.killbill.billing.payment.dao.PaymentDao;
-import org.killbill.billing.payment.plugin.api.PaymentPluginApi;
import org.killbill.billing.util.UUIDs;
import org.killbill.billing.util.callcontext.CallContext;
import org.killbill.billing.util.callcontext.CallOrigin;
@@ -57,17 +55,20 @@ abstract class CompletionTaskBase<T> implements Runnable {
protected final PaymentStateMachineHelper paymentStateMachineHelper;
protected final PaymentControlStateMachineHelper retrySMHelper;
protected final AccountInternalApi accountInternalApi;
- protected final OSGIServiceRegistration<PaymentPluginApi> pluginRegistry;
protected final GlobalLocker locker;
protected NotificationQueue janitorQueue;
private volatile boolean isStopped;
- public CompletionTaskBase(final InternalCallContextFactory internalCallContextFactory, final PaymentConfig paymentConfig,
- final PaymentDao paymentDao, final Clock clock, final PaymentStateMachineHelper paymentStateMachineHelper,
- final PaymentControlStateMachineHelper retrySMHelper, final AccountInternalApi accountInternalApi,
- final OSGIServiceRegistration<PaymentPluginApi> pluginRegistry, final GlobalLocker locker) {
+ public CompletionTaskBase(final InternalCallContextFactory internalCallContextFactory,
+ final PaymentConfig paymentConfig,
+ final PaymentDao paymentDao,
+ final Clock clock,
+ final PaymentStateMachineHelper paymentStateMachineHelper,
+ final PaymentControlStateMachineHelper retrySMHelper,
+ final AccountInternalApi accountInternalApi,
+ final GlobalLocker locker) {
this.internalCallContextFactory = internalCallContextFactory;
this.paymentConfig = paymentConfig;
this.paymentDao = paymentDao;
@@ -75,7 +76,6 @@ abstract class CompletionTaskBase<T> implements Runnable {
this.paymentStateMachineHelper = paymentStateMachineHelper;
this.retrySMHelper = retrySMHelper;
this.accountInternalApi = accountInternalApi;
- this.pluginRegistry = pluginRegistry;
this.locker = locker;
this.isStopped = false;
}
diff --git a/payment/src/main/java/org/killbill/billing/payment/core/janitor/IncompletePaymentAttemptTask.java b/payment/src/main/java/org/killbill/billing/payment/core/janitor/IncompletePaymentAttemptTask.java
index 568ec94..400b332 100644
--- a/payment/src/main/java/org/killbill/billing/payment/core/janitor/IncompletePaymentAttemptTask.java
+++ b/payment/src/main/java/org/killbill/billing/payment/core/janitor/IncompletePaymentAttemptTask.java
@@ -1,6 +1,6 @@
/*
- * Copyright 2014-2016 Groupon, Inc
- * Copyright 2014-2016 The Billing Project, LLC
+ * Copyright 2014-2017 Groupon, Inc
+ * Copyright 2014-2017 The Billing Project, LLC
*
* The Billing Project licenses this file to you under the Apache License, version 2.0
* (the "License"); you may not use this file except in compliance with the
@@ -28,7 +28,6 @@ import org.killbill.billing.account.api.AccountInternalApi;
import org.killbill.billing.callcontext.InternalCallContext;
import org.killbill.billing.callcontext.InternalTenantContext;
import org.killbill.billing.events.PaymentInternalEvent;
-import org.killbill.billing.osgi.api.OSGIServiceRegistration;
import org.killbill.billing.payment.api.PaymentApiException;
import org.killbill.billing.payment.api.TransactionStatus;
import org.killbill.billing.payment.core.sm.PaymentControlStateMachineHelper;
@@ -40,7 +39,6 @@ import org.killbill.billing.payment.dao.PaymentDao;
import org.killbill.billing.payment.dao.PaymentTransactionModelDao;
import org.killbill.billing.payment.dao.PluginPropertySerializer;
import org.killbill.billing.payment.dao.PluginPropertySerializer.PluginPropertySerializerException;
-import org.killbill.billing.payment.plugin.api.PaymentPluginApi;
import org.killbill.billing.util.callcontext.CallContext;
import org.killbill.billing.util.callcontext.InternalCallContextFactory;
import org.killbill.billing.util.config.definition.PaymentConfig;
@@ -82,9 +80,8 @@ public class IncompletePaymentAttemptTask extends CompletionTaskBase<PaymentAtte
final PaymentControlStateMachineHelper retrySMHelper,
final AccountInternalApi accountInternalApi,
final PluginControlPaymentAutomatonRunner pluginControlledPaymentAutomatonRunner,
- final OSGIServiceRegistration<PaymentPluginApi> pluginRegistry,
final GlobalLocker locker) {
- super(internalCallContextFactory, paymentConfig, paymentDao, clock, paymentStateMachineHelper, retrySMHelper, accountInternalApi, pluginRegistry, locker);
+ super(internalCallContextFactory, paymentConfig, paymentDao, clock, paymentStateMachineHelper, retrySMHelper, accountInternalApi, locker);
this.pluginControlledPaymentAutomatonRunner = pluginControlledPaymentAutomatonRunner;
}
diff --git a/payment/src/main/java/org/killbill/billing/payment/core/janitor/IncompletePaymentTransactionTask.java b/payment/src/main/java/org/killbill/billing/payment/core/janitor/IncompletePaymentTransactionTask.java
index 72f050f..b49b5f7 100644
--- a/payment/src/main/java/org/killbill/billing/payment/core/janitor/IncompletePaymentTransactionTask.java
+++ b/payment/src/main/java/org/killbill/billing/payment/core/janitor/IncompletePaymentTransactionTask.java
@@ -1,6 +1,6 @@
/*
- * Copyright 2014-2016 Groupon, Inc
- * Copyright 2014-2016 The Billing Project, LLC
+ * Copyright 2014-2017 Groupon, Inc
+ * Copyright 2014-2017 The Billing Project, LLC
*
* The Billing Project licenses this file to you under the Apache License, version 2.0
* (the "License"); you may not use this file except in compliance with the
@@ -31,14 +31,13 @@ import org.killbill.billing.callcontext.InternalCallContext;
import org.killbill.billing.callcontext.InternalTenantContext;
import org.killbill.billing.catalog.api.Currency;
import org.killbill.billing.events.PaymentInternalEvent;
-import org.killbill.billing.osgi.api.OSGIServiceRegistration;
import org.killbill.billing.payment.api.PluginProperty;
import org.killbill.billing.payment.api.TransactionStatus;
+import org.killbill.billing.payment.core.PaymentPluginServiceRegistration;
import org.killbill.billing.payment.core.PaymentTransactionInfoPluginConverter;
import org.killbill.billing.payment.core.sm.PaymentControlStateMachineHelper;
import org.killbill.billing.payment.core.sm.PaymentStateMachineHelper;
import org.killbill.billing.payment.dao.PaymentDao;
-import org.killbill.billing.payment.dao.PaymentMethodModelDao;
import org.killbill.billing.payment.dao.PaymentModelDao;
import org.killbill.billing.payment.dao.PaymentTransactionModelDao;
import org.killbill.billing.payment.plugin.api.PaymentPluginApi;
@@ -59,7 +58,6 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.common.annotations.VisibleForTesting;
-import com.google.common.base.Preconditions;
import com.google.common.base.Predicate;
import com.google.common.base.Supplier;
import com.google.common.collect.ImmutableList;
@@ -74,12 +72,20 @@ public class IncompletePaymentTransactionTask extends CompletionTaskBase<Payment
.add(TransactionStatus.UNKNOWN)
.build();
+ private final PaymentPluginServiceRegistration paymentPluginServiceRegistration;
+
@Inject
- public IncompletePaymentTransactionTask(final InternalCallContextFactory internalCallContextFactory, final PaymentConfig paymentConfig,
- final PaymentDao paymentDao, final Clock clock,
- final PaymentStateMachineHelper paymentStateMachineHelper, final PaymentControlStateMachineHelper retrySMHelper, final AccountInternalApi accountInternalApi,
- final OSGIServiceRegistration<PaymentPluginApi> pluginRegistry, final GlobalLocker locker) {
- super(internalCallContextFactory, paymentConfig, paymentDao, clock, paymentStateMachineHelper, retrySMHelper, accountInternalApi, pluginRegistry, locker);
+ public IncompletePaymentTransactionTask(final InternalCallContextFactory internalCallContextFactory,
+ final PaymentConfig paymentConfig,
+ final PaymentDao paymentDao,
+ final Clock clock,
+ final PaymentStateMachineHelper paymentStateMachineHelper,
+ final PaymentControlStateMachineHelper retrySMHelper,
+ final AccountInternalApi accountInternalApi,
+ final PaymentPluginServiceRegistration paymentPluginServiceRegistration,
+ final GlobalLocker locker) {
+ super(internalCallContextFactory, paymentConfig, paymentDao, clock, paymentStateMachineHelper, retrySMHelper, accountInternalApi, locker);
+ this.paymentPluginServiceRegistration = paymentPluginServiceRegistration;
}
@Override
@@ -113,9 +119,6 @@ public class IncompletePaymentTransactionTask extends CompletionTaskBase<Payment
final TenantContext tenantContext = internalCallContextFactory.createTenantContext(internalTenantContext);
final PaymentModelDao payment = paymentDao.getPayment(rehydratedPaymentTransaction.getPaymentId(), internalTenantContext);
- final PaymentMethodModelDao paymentMethod = paymentDao.getPaymentMethod(payment.getPaymentMethodId(), internalTenantContext);
- final PaymentPluginApi paymentPluginApi = getPaymentPluginApi(payment, paymentMethod.getPluginName());
-
final PaymentTransactionInfoPlugin undefinedPaymentTransaction = new DefaultNoOpPaymentInfoPlugin(payment.getId(),
rehydratedPaymentTransaction.getId(),
rehydratedPaymentTransaction.getTransactionType(),
@@ -128,6 +131,7 @@ public class IncompletePaymentTransactionTask extends CompletionTaskBase<Payment
null);
PaymentTransactionInfoPlugin paymentTransactionInfoPlugin;
try {
+ final PaymentPluginApi paymentPluginApi = paymentPluginServiceRegistration.getPaymentPluginApi(payment.getPaymentMethodId(), false, internalTenantContext);
final List<PaymentTransactionInfoPlugin> result = paymentPluginApi.getPaymentInfo(payment.getAccountId(), payment.getId(), ImmutableList.<PluginProperty>of(), tenantContext);
paymentTransactionInfoPlugin = Iterables.tryFind(result, new Predicate<PaymentTransactionInfoPlugin>() {
@Override
@@ -261,12 +265,6 @@ public class IncompletePaymentTransactionTask extends CompletionTaskBase<Payment
return (newTransactionStatus != TransactionStatus.UNKNOWN) ? newTransactionStatus : currentTransactionStatus;
}
- private PaymentPluginApi getPaymentPluginApi(final PaymentModelDao item, final String pluginName) {
- final PaymentPluginApi pluginApi = pluginRegistry.getServiceForName(pluginName);
- Preconditions.checkState(pluginApi != null, "Janitor IncompletePaymentTransactionTask cannot retrieve PaymentPluginApi for plugin %s (payment id %s), skipping", pluginName, item.getId());
- return pluginApi;
- }
-
@VisibleForTesting
DateTime getNextNotificationTime(final Integer attemptNumber, final InternalTenantContext tenantContext) {
diff --git a/payment/src/main/java/org/killbill/billing/payment/core/PaymentGatewayProcessor.java b/payment/src/main/java/org/killbill/billing/payment/core/PaymentGatewayProcessor.java
index ea40106..feae745 100644
--- a/payment/src/main/java/org/killbill/billing/payment/core/PaymentGatewayProcessor.java
+++ b/payment/src/main/java/org/killbill/billing/payment/core/PaymentGatewayProcessor.java
@@ -1,6 +1,6 @@
/*
- * Copyright 2014-2015 Groupon, Inc
- * Copyright 2014-2015 The Billing Project, LLC
+ * Copyright 2014-2017 Groupon, Inc
+ * Copyright 2014-2017 The Billing Project, LLC
*
* The Billing Project licenses this file to you under the Apache License, version 2.0
* (the "License"); you may not use this file except in compliance with the
@@ -29,7 +29,6 @@ import org.killbill.billing.account.api.Account;
import org.killbill.billing.account.api.AccountInternalApi;
import org.killbill.billing.callcontext.InternalCallContext;
import org.killbill.billing.invoice.api.InvoiceInternalApi;
-import org.killbill.billing.osgi.api.OSGIServiceRegistration;
import org.killbill.billing.payment.api.PaymentApiException;
import org.killbill.billing.payment.api.PluginProperty;
import org.killbill.billing.payment.dao.PaymentDao;
@@ -48,7 +47,7 @@ import org.killbill.billing.util.config.definition.PaymentConfig;
import org.killbill.clock.Clock;
import org.killbill.commons.locker.GlobalLocker;
-import com.google.common.base.Objects;
+import com.google.common.base.MoreObjects;
import static org.killbill.billing.payment.dispatcher.PaymentPluginDispatcher.dispatchWithExceptionHandling;
@@ -58,7 +57,7 @@ public class PaymentGatewayProcessor extends ProcessorBase {
private final PluginDispatcher<GatewayNotification> paymentPluginNotificationDispatcher;
@Inject
- public PaymentGatewayProcessor(final OSGIServiceRegistration<PaymentPluginApi> pluginRegistry,
+ public PaymentGatewayProcessor(final PaymentPluginServiceRegistration paymentPluginServiceRegistration,
final AccountInternalApi accountUserApi,
final InvoiceInternalApi invoiceApi,
final TagInternalApi tagUserApi,
@@ -68,26 +67,27 @@ public class PaymentGatewayProcessor extends ProcessorBase {
final PaymentExecutors executors,
final InternalCallContextFactory internalCallContextFactory,
final Clock clock) {
- super(pluginRegistry, accountUserApi, paymentDao, tagUserApi, locker, internalCallContextFactory, invoiceApi, clock);
+ super(paymentPluginServiceRegistration, accountUserApi, paymentDao, tagUserApi, locker, internalCallContextFactory, invoiceApi, clock);
final long paymentPluginTimeoutSec = TimeUnit.SECONDS.convert(paymentConfig.getPaymentPluginTimeout().getPeriod(), paymentConfig.getPaymentPluginTimeout().getUnit());
this.paymentPluginFormDispatcher = new PluginDispatcher<HostedPaymentPageFormDescriptor>(paymentPluginTimeoutSec, executors);
this.paymentPluginNotificationDispatcher = new PluginDispatcher<GatewayNotification>(paymentPluginTimeoutSec, executors);
}
public GatewayNotification processNotification(final boolean shouldDispatch, final String notification, final UUID paymentMethodId, final Iterable<PluginProperty> properties, final CallContext callContext) throws PaymentApiException {
- final String pluginName = getPaymentProviderPluginName(paymentMethodId, internalCallContextFactory.createInternalCallContext(paymentMethodId, ObjectType.PAYMENT_METHOD, callContext));
+ final InternalCallContext internalCallContext = internalCallContextFactory.createInternalCallContext(paymentMethodId, ObjectType.PAYMENT_METHOD, callContext);
+ final String pluginName = getPaymentMethodById(paymentMethodId, true, internalCallContext).getPluginName();
return processNotification(shouldDispatch, notification, pluginName, properties, callContext);
}
public GatewayNotification processNotification(final boolean shouldDispatch, final String notification, final String pluginName, final Iterable<PluginProperty> properties, final CallContext callContext) throws PaymentApiException {
+ final PaymentPluginApi plugin = getPaymentPluginApi(pluginName);
+
if (shouldDispatch) {
return dispatchWithExceptionHandling(null,
pluginName,
new Callable<PluginDispatcherReturnType<GatewayNotification>>() {
@Override
public PluginDispatcherReturnType<GatewayNotification> call() throws PaymentApiException {
- final PaymentPluginApi plugin = getPaymentPluginApi(pluginName);
-
try {
final GatewayNotification result = plugin.processNotification(notification, properties, callContext);
return PluginDispatcher.createPluginDispatcherReturnType(result == null ? new DefaultNoOpGatewayNotification() : result);
@@ -97,7 +97,6 @@ public class PaymentGatewayProcessor extends ProcessorBase {
}
}, paymentPluginNotificationDispatcher);
} else {
- final PaymentPluginApi plugin = getPaymentPluginApi(pluginName);
try {
return plugin.processNotification(notification, properties, callContext);
} catch (final PaymentPluginApiException e) {
@@ -107,7 +106,8 @@ public class PaymentGatewayProcessor extends ProcessorBase {
}
public HostedPaymentPageFormDescriptor buildFormDescriptor(final boolean shouldDispatch, final Account account, final UUID paymentMethodId, final Iterable<PluginProperty> customFields, final Iterable<PluginProperty> properties, final CallContext callContext, final InternalCallContext internalCallContext) throws PaymentApiException {
- final String pluginName = getPaymentProviderPluginName(paymentMethodId, internalCallContext);
+ final String pluginName = getPaymentMethodById(paymentMethodId, true, internalCallContext).getPluginName();
+ final PaymentPluginApi plugin = getPaymentPluginApi(pluginName);
if (shouldDispatch) {
return dispatchWithExceptionHandling(account,
@@ -115,20 +115,17 @@ public class PaymentGatewayProcessor extends ProcessorBase {
new Callable<PluginDispatcherReturnType<HostedPaymentPageFormDescriptor>>() {
@Override
public PluginDispatcherReturnType<HostedPaymentPageFormDescriptor> call() throws PaymentApiException {
- final PaymentPluginApi plugin = getPaymentPluginApi(pluginName);
-
try {
final HostedPaymentPageFormDescriptor result = plugin.buildFormDescriptor(account.getId(), customFields, properties, callContext);
return PluginDispatcher.createPluginDispatcherReturnType(result == null ? new DefaultNoOpHostedPaymentPageFormDescriptor(account.getId()) : result);
} catch (final RuntimeException e) {
- throw new PaymentApiException(e, ErrorCode.PAYMENT_INTERNAL_ERROR, Objects.firstNonNull(e.getMessage(), ""));
+ throw new PaymentApiException(e, ErrorCode.PAYMENT_INTERNAL_ERROR, MoreObjects.firstNonNull(e.getMessage(), ""));
} catch (final PaymentPluginApiException e) {
throw new PaymentApiException(e, ErrorCode.PAYMENT_PLUGIN_EXCEPTION, e.getErrorMessage());
}
}
}, paymentPluginFormDispatcher);
} else {
- final PaymentPluginApi plugin = getPaymentPluginApi(pluginName);
try {
return plugin.buildFormDescriptor(account.getId(), customFields, properties, callContext);
} catch (final PaymentPluginApiException e) {
diff --git a/payment/src/main/java/org/killbill/billing/payment/core/PaymentMethodProcessor.java b/payment/src/main/java/org/killbill/billing/payment/core/PaymentMethodProcessor.java
index ebc4fe5..a0203d1 100644
--- a/payment/src/main/java/org/killbill/billing/payment/core/PaymentMethodProcessor.java
+++ b/payment/src/main/java/org/killbill/billing/payment/core/PaymentMethodProcessor.java
@@ -1,7 +1,7 @@
/*
* Copyright 2010-2013 Ning, Inc.
- * Copyright 2014-2015 Groupon, Inc
- * Copyright 2014-2015 The Billing Project, LLC
+ * Copyright 2014-2017 Groupon, Inc
+ * Copyright 2014-2017 The Billing Project, LLC
*
* The Billing Project licenses this file to you under the Apache License, version 2.0
* (the "License"); you may not use this file except in compliance with the
@@ -34,7 +34,6 @@ import org.killbill.billing.account.api.AccountInternalApi;
import org.killbill.billing.callcontext.InternalCallContext;
import org.killbill.billing.callcontext.InternalTenantContext;
import org.killbill.billing.invoice.api.InvoiceInternalApi;
-import org.killbill.billing.osgi.api.OSGIServiceRegistration;
import org.killbill.billing.payment.api.DefaultPaymentMethod;
import org.killbill.billing.payment.api.PaymentApiException;
import org.killbill.billing.payment.api.PaymentMethod;
@@ -66,12 +65,12 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.common.base.Function;
-import com.google.common.base.Objects;
+import com.google.common.base.MoreObjects;
import com.google.common.base.Predicate;
import com.google.common.collect.Collections2;
import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
-import com.google.common.collect.Iterators;
import com.google.inject.Inject;
import static org.killbill.billing.payment.dispatcher.PaymentPluginDispatcher.dispatchWithExceptionHandling;
@@ -87,7 +86,7 @@ public class PaymentMethodProcessor extends ProcessorBase {
private final PaymentConfig paymentConfig;
@Inject
- public PaymentMethodProcessor(final OSGIServiceRegistration<PaymentPluginApi> pluginRegistry,
+ public PaymentMethodProcessor(final PaymentPluginServiceRegistration paymentPluginServiceRegistration,
final AccountInternalApi accountInternalApi,
final InvoiceInternalApi invoiceApi,
final PaymentDao paymentDao,
@@ -97,7 +96,7 @@ public class PaymentMethodProcessor extends ProcessorBase {
final PaymentExecutors executors,
final InternalCallContextFactory internalCallContextFactory,
final Clock clock) {
- super(pluginRegistry, accountInternalApi, paymentDao, tagUserApi, locker, internalCallContextFactory, invoiceApi, clock);
+ super(paymentPluginServiceRegistration, accountInternalApi, paymentDao, tagUserApi, locker, internalCallContextFactory, invoiceApi, clock);
final long paymentPluginTimeoutSec = TimeUnit.SECONDS.convert(paymentConfig.getPaymentPluginTimeout().getPeriod(), paymentConfig.getPaymentPluginTimeout().getUnit());
this.paymentConfig = paymentConfig;
this.uuidPluginNotificationDispatcher = new PluginDispatcher<UUID>(paymentPluginTimeoutSec, executors);
@@ -186,7 +185,13 @@ public class PaymentMethodProcessor extends ProcessorBase {
if (paymentMethodPlugin != null && paymentMethodPlugin.getExternalPaymentMethodId() != null) {
// An external payment method id is set but make sure it doesn't conflict with an existing one
final String externalKey = paymentMethodPlugin.getExternalPaymentMethodId();
- return paymentDao.getPaymentMethodByExternalKeyIncludedDeleted(externalKey, context) == null ? externalKey : null;
+ try {
+ @SuppressWarnings("unused")
+ final PaymentMethodModelDao paymentMethodModelDao = getPaymentMethodByExternalKey(externalKey, true, context);
+ return null;
+ } catch (final PaymentApiException e) {
+ return externalKey;
+ }
} else {
return null;
}
@@ -206,20 +211,13 @@ public class PaymentMethodProcessor extends ProcessorBase {
public PaymentMethod getPaymentMethodById(final UUID paymentMethodId, final boolean includedDeleted, final boolean withPluginInfo, final Iterable<PluginProperty> properties, final TenantContext tenantContext, final InternalTenantContext context)
throws PaymentApiException {
- final PaymentMethodModelDao paymentMethodModel = includedDeleted ? paymentDao.getPaymentMethodIncludedDeleted(paymentMethodId, context) : paymentDao.getPaymentMethod(paymentMethodId, context);
- if (paymentMethodModel == null) {
- throw new PaymentApiException(ErrorCode.PAYMENT_NO_SUCH_PAYMENT_METHOD, paymentMethodId);
- }
-
+ final PaymentMethodModelDao paymentMethodModel = getPaymentMethodById(paymentMethodId, includedDeleted, context);
return buildDefaultPaymentMethod(paymentMethodModel, withPluginInfo, properties, tenantContext, context);
}
public PaymentMethod getPaymentMethodByExternalKey(final String paymentMethodExternalKey, final boolean includedDeleted, final boolean withPluginInfo, final Iterable<PluginProperty> properties, final TenantContext tenantContext, final InternalTenantContext context)
throws PaymentApiException {
- final PaymentMethodModelDao paymentMethodModel = includedDeleted ? paymentDao.getPaymentMethodByExternalKeyIncludedDeleted(paymentMethodExternalKey, context) : paymentDao.getPaymentMethodByExternalKey(paymentMethodExternalKey, context);
- if (paymentMethodModel == null) {
- throw new PaymentApiException(ErrorCode.PAYMENT_NO_SUCH_PAYMENT_METHOD, paymentMethodExternalKey);
- }
+ final PaymentMethodModelDao paymentMethodModel = getPaymentMethodByExternalKey(paymentMethodExternalKey, includedDeleted, context);
return buildDefaultPaymentMethod(paymentMethodModel, withPluginInfo, properties, tenantContext, context);
}
@@ -318,7 +316,7 @@ public class PaymentMethodProcessor extends ProcessorBase {
);
} catch (final PaymentApiException e) {
log.warn("Unable to search through payment methods", e);
- return new DefaultPagination<PaymentMethod>(offset, limit, null, null, Iterators.<PaymentMethod>emptyIterator());
+ return new DefaultPagination<PaymentMethod>(offset, limit, null, null, ImmutableSet.<PaymentMethod>of().iterator());
}
}
}
@@ -407,10 +405,8 @@ public class PaymentMethodProcessor extends ProcessorBase {
@Override
public PluginDispatcherReturnType<Void> doOperation() throws PaymentApiException {
- final PaymentMethodModelDao paymentMethodModel = paymentDao.getPaymentMethod(paymentMethodId, context);
- if (paymentMethodModel == null) {
- throw new PaymentApiException(ErrorCode.PAYMENT_NO_SUCH_PAYMENT_METHOD, paymentMethodId);
- }
+ @SuppressWarnings("unused")
+ final PaymentMethodModelDao paymentMethodModel = getPaymentMethodById(paymentMethodId, false, context);
try {
// Note: account.getPaymentMethodId() may be null
@@ -425,7 +421,7 @@ public class PaymentMethodProcessor extends ProcessorBase {
accountInternalApi.removePaymentMethod(account.getId(), context);
}
}
- final PaymentPluginApi pluginApi = getPluginApi(paymentMethodId, context);
+ final PaymentPluginApi pluginApi = getPaymentProviderPlugin(paymentMethodId, false, context);
pluginApi.deletePaymentMethod(account.getId(), paymentMethodId, properties, callContext);
paymentDao.deletedPaymentMethod(paymentMethodId, context);
return PluginDispatcher.createPluginDispatcherReturnType(null);
@@ -437,7 +433,7 @@ public class PaymentMethodProcessor extends ProcessorBase {
}
});
} catch (final Exception e) {
- throw new PaymentApiException(e, ErrorCode.PAYMENT_INTERNAL_ERROR, Objects.firstNonNull(e.getMessage(), ""));
+ throw new PaymentApiException(e, ErrorCode.PAYMENT_INTERNAL_ERROR, MoreObjects.firstNonNull(e.getMessage(), ""));
}
}
@@ -448,17 +444,14 @@ public class PaymentMethodProcessor extends ProcessorBase {
@Override
public PluginDispatcherReturnType<Void> doOperation() throws PaymentApiException {
- final PaymentMethodModelDao paymentMethodModel = paymentDao.getPaymentMethod(paymentMethodId, context);
- if (paymentMethodModel == null) {
- throw new PaymentApiException(ErrorCode.PAYMENT_NO_SUCH_PAYMENT_METHOD, paymentMethodId);
- }
+ final PaymentMethodModelDao paymentMethodModel = getPaymentMethodById(paymentMethodId, false, context);
if (!paymentMethodModel.getAccountId().equals(account.getId())) {
throw new PaymentApiException(ErrorCode.PAYMENT_METHOD_DIFFERENT_ACCOUNT_ID, paymentMethodId);
}
try {
- final PaymentPluginApi pluginApi = getPluginApi(paymentMethodId, context);
+ final PaymentPluginApi pluginApi = getPaymentProviderPlugin(paymentMethodId, false, context);
pluginApi.setDefaultPaymentMethod(account.getId(), paymentMethodId, properties, callContext);
accountInternalApi.updatePaymentMethod(account.getId(), paymentMethodId, context);
@@ -471,17 +464,8 @@ public class PaymentMethodProcessor extends ProcessorBase {
}
});
} catch (final Exception e) {
- throw new PaymentApiException(e, ErrorCode.PAYMENT_INTERNAL_ERROR, Objects.firstNonNull(e.getMessage(), ""));
- }
- }
-
- private PaymentPluginApi getPluginApi(final UUID paymentMethodId, final InternalTenantContext context)
- throws PaymentApiException {
- final PaymentMethodModelDao paymentMethod = paymentDao.getPaymentMethod(paymentMethodId, context);
- if (paymentMethod == null) {
- throw new PaymentApiException(ErrorCode.PAYMENT_NO_SUCH_PAYMENT_METHOD, paymentMethodId);
+ throw new PaymentApiException(e, ErrorCode.PAYMENT_INTERNAL_ERROR, MoreObjects.firstNonNull(e.getMessage(), ""));
}
- return getPaymentPluginApi(paymentMethod.getPluginName());
}
/**
@@ -563,7 +547,7 @@ public class PaymentMethodProcessor extends ProcessorBase {
});
return result.getReturnType();
} catch (final Exception e) {
- throw new PaymentApiException(e, ErrorCode.PAYMENT_INTERNAL_ERROR, Objects.firstNonNull(e.getMessage(), ""));
+ throw new PaymentApiException(e, ErrorCode.PAYMENT_INTERNAL_ERROR, MoreObjects.firstNonNull(e.getMessage(), ""));
}
}
@@ -580,7 +564,7 @@ public class PaymentMethodProcessor extends ProcessorBase {
boolean shouldUpdateDefaultPaymentMethod = true;
if (account.getPaymentMethodId() != null) {
- final PaymentMethodModelDao currentDefaultPaymentMethod = paymentDao.getPaymentMethod(account.getPaymentMethodId(), context);
+ final PaymentMethodModelDao currentDefaultPaymentMethod = getPaymentMethodById(account.getPaymentMethodId(), false, context);
shouldUpdateDefaultPaymentMethod = pluginName.equals(currentDefaultPaymentMethod.getPluginName());
}
if (shouldUpdateDefaultPaymentMethod) {
diff --git a/payment/src/main/java/org/killbill/billing/payment/core/PaymentPluginServiceRegistration.java b/payment/src/main/java/org/killbill/billing/payment/core/PaymentPluginServiceRegistration.java
new file mode 100644
index 0000000..016be05
--- /dev/null
+++ b/payment/src/main/java/org/killbill/billing/payment/core/PaymentPluginServiceRegistration.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright 2017 Groupon, Inc
+ * Copyright 2017 The Billing Project, LLC
+ *
+ * The Billing Project licenses this file to you under the Apache License, version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.killbill.billing.payment.core;
+
+import java.util.Set;
+import java.util.UUID;
+
+import javax.inject.Inject;
+
+import org.killbill.billing.ErrorCode;
+import org.killbill.billing.callcontext.InternalTenantContext;
+import org.killbill.billing.osgi.api.OSGIServiceRegistration;
+import org.killbill.billing.payment.api.PaymentApiException;
+import org.killbill.billing.payment.dao.PaymentDao;
+import org.killbill.billing.payment.dao.PaymentMethodModelDao;
+import org.killbill.billing.payment.plugin.api.PaymentPluginApi;
+
+public class PaymentPluginServiceRegistration {
+
+ private final PaymentDao paymentDao;
+ private final OSGIServiceRegistration<PaymentPluginApi> pluginRegistry;
+
+ @Inject
+ public PaymentPluginServiceRegistration(final PaymentDao paymentDao, final OSGIServiceRegistration<PaymentPluginApi> pluginRegistry) {
+ this.paymentDao = paymentDao;
+ this.pluginRegistry = pluginRegistry;
+ }
+
+ public Set<String> getAvailablePlugins() {
+ return pluginRegistry.getAllServices();
+ }
+
+ public PaymentMethodModelDao getPaymentMethodById(final UUID paymentMethodId, final boolean includedDeleted, final InternalTenantContext context) throws PaymentApiException {
+ final PaymentMethodModelDao paymentMethodModel;
+ if (includedDeleted) {
+ paymentMethodModel = paymentDao.getPaymentMethodIncludedDeleted(paymentMethodId, context);
+ } else {
+ paymentMethodModel = paymentDao.getPaymentMethod(paymentMethodId, context);
+ }
+
+ if (paymentMethodModel == null) {
+ throw new PaymentApiException(ErrorCode.PAYMENT_NO_SUCH_PAYMENT_METHOD, paymentMethodId);
+ }
+
+ return paymentMethodModel;
+ }
+
+ public PaymentMethodModelDao getPaymentMethodByExternalKey(final String paymentMethodExternalKey, final boolean includedDeleted, final InternalTenantContext context) throws PaymentApiException {
+ final PaymentMethodModelDao paymentMethodModel;
+ if (includedDeleted) {
+ paymentMethodModel = paymentDao.getPaymentMethodByExternalKeyIncludedDeleted(paymentMethodExternalKey, context);
+ } else {
+ paymentMethodModel = paymentDao.getPaymentMethodByExternalKey(paymentMethodExternalKey, context);
+ }
+
+ if (paymentMethodModel == null) {
+ throw new PaymentApiException(ErrorCode.PAYMENT_NO_SUCH_PAYMENT_METHOD, paymentMethodExternalKey);
+ }
+ return paymentMethodModel;
+ }
+
+ public PaymentPluginApi getPaymentPluginApi(final UUID paymentMethodId, final boolean includedDeleted, final InternalTenantContext context) throws PaymentApiException {
+ final PaymentMethodModelDao paymentMethodModelDao = getPaymentMethodById(paymentMethodId, includedDeleted, context);
+ return getPaymentPluginApi(paymentMethodModelDao.getPluginName());
+ }
+
+ public PaymentPluginApi getPaymentPluginApi(final String pluginName) throws PaymentApiException {
+ final PaymentPluginApi pluginApi = pluginRegistry.getServiceForName(pluginName);
+ if (pluginApi == null) {
+ throw new PaymentApiException(ErrorCode.PAYMENT_NO_SUCH_PAYMENT_PLUGIN, pluginName);
+ }
+ return pluginApi;
+ }
+}
diff --git a/payment/src/main/java/org/killbill/billing/payment/core/PaymentProcessor.java b/payment/src/main/java/org/killbill/billing/payment/core/PaymentProcessor.java
index b35e99a..4db7ea5 100644
--- a/payment/src/main/java/org/killbill/billing/payment/core/PaymentProcessor.java
+++ b/payment/src/main/java/org/killbill/billing/payment/core/PaymentProcessor.java
@@ -41,7 +41,6 @@ import org.killbill.billing.callcontext.InternalCallContext;
import org.killbill.billing.callcontext.InternalTenantContext;
import org.killbill.billing.catalog.api.Currency;
import org.killbill.billing.invoice.api.InvoiceInternalApi;
-import org.killbill.billing.osgi.api.OSGIServiceRegistration;
import org.killbill.billing.payment.api.DefaultPayment;
import org.killbill.billing.payment.api.DefaultPaymentAttempt;
import org.killbill.billing.payment.api.DefaultPaymentTransaction;
@@ -93,7 +92,6 @@ import com.google.common.base.Predicate;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
-import com.google.common.collect.Iterators;
import com.google.common.collect.Lists;
import com.google.common.collect.Ordering;
@@ -114,7 +112,7 @@ public class PaymentProcessor extends ProcessorBase {
@Inject
- public PaymentProcessor(final OSGIServiceRegistration<PaymentPluginApi> pluginRegistry,
+ public PaymentProcessor(final PaymentPluginServiceRegistration paymentPluginServiceRegistration,
final AccountInternalApi accountUserApi,
final InvoiceInternalApi invoiceApi,
final TagInternalApi tagUserApi,
@@ -125,55 +123,55 @@ public class PaymentProcessor extends ProcessorBase {
final IncompletePaymentTransactionTask incompletePaymentTransactionTask,
final NotificationQueueService notificationQueueService,
final Clock clock) {
- super(pluginRegistry, accountUserApi, paymentDao, tagUserApi, locker, internalCallContextFactory, invoiceApi, clock);
+ super(paymentPluginServiceRegistration, accountUserApi, paymentDao, tagUserApi, locker, internalCallContextFactory, invoiceApi, clock);
this.paymentAutomatonRunner = paymentAutomatonRunner;
this.incompletePaymentTransactionTask = incompletePaymentTransactionTask;
this.notificationQueueService = notificationQueueService;
}
public Payment createAuthorization(final boolean isApiPayment, @Nullable final UUID attemptId, final Account account, @Nullable final UUID paymentMethodId, @Nullable final UUID paymentId, final BigDecimal amount, final Currency currency,
- @Nullable final String paymentExternalKey, @Nullable final String paymentTransactionExternalKey, final boolean shouldLockAccountAndDispatch,
+ @Nullable final String paymentExternalKey, @Nullable final String paymentTransactionExternalKey, @Nullable final UUID paymentIdForNewPayment, @Nullable final UUID paymentTransactionIdForNewPaymentTransaction, final boolean shouldLockAccountAndDispatch,
final Iterable<PluginProperty> properties, final CallContext callContext, final InternalCallContext internalCallContext) throws PaymentApiException {
- return performOperation(isApiPayment, attemptId, TransactionType.AUTHORIZE, account, paymentMethodId, paymentId, null, amount, currency, paymentExternalKey, paymentTransactionExternalKey, shouldLockAccountAndDispatch, null, properties, callContext, internalCallContext);
+ return performOperation(isApiPayment, attemptId, TransactionType.AUTHORIZE, account, paymentMethodId, paymentId, null, amount, currency, paymentExternalKey, paymentTransactionExternalKey, paymentIdForNewPayment, paymentTransactionIdForNewPaymentTransaction, shouldLockAccountAndDispatch, null, properties, callContext, internalCallContext);
}
public Payment createCapture(final boolean isApiPayment, @Nullable final UUID attemptId, final Account account, final UUID paymentId, final BigDecimal amount, final Currency currency,
- @Nullable final String paymentTransactionExternalKey, final boolean shouldLockAccountAndDispatch,
+ @Nullable final String paymentTransactionExternalKey, @Nullable final UUID paymentTransactionIdForNewPaymentTransaction, final boolean shouldLockAccountAndDispatch,
final Iterable<PluginProperty> properties, final CallContext callContext, final InternalCallContext internalCallContext) throws PaymentApiException {
- return performOperation(isApiPayment, attemptId, TransactionType.CAPTURE, account, null, paymentId, null, amount, currency, null, paymentTransactionExternalKey, shouldLockAccountAndDispatch, null, properties, callContext, internalCallContext);
+ return performOperation(isApiPayment, attemptId, TransactionType.CAPTURE, account, null, paymentId, null, amount, currency, null, paymentTransactionExternalKey, null, paymentTransactionIdForNewPaymentTransaction, shouldLockAccountAndDispatch, null, properties, callContext, internalCallContext);
}
public Payment createPurchase(final boolean isApiPayment, @Nullable final UUID attemptId, final Account account, @Nullable final UUID paymentMethodId, @Nullable final UUID paymentId, final BigDecimal amount, final Currency currency,
- @Nullable final String paymentExternalKey, @Nullable final String paymentTransactionExternalKey, final boolean shouldLockAccountAndDispatch,
+ @Nullable final String paymentExternalKey, @Nullable final String paymentTransactionExternalKey, @Nullable final UUID paymentIdForNewPayment, @Nullable final UUID paymentTransactionIdForNewPaymentTransaction, final boolean shouldLockAccountAndDispatch,
final Iterable<PluginProperty> properties, final CallContext callContext, final InternalCallContext internalCallContext) throws PaymentApiException {
- return performOperation(isApiPayment, attemptId, TransactionType.PURCHASE, account, paymentMethodId, paymentId, null, amount, currency, paymentExternalKey, paymentTransactionExternalKey, shouldLockAccountAndDispatch, null, properties, callContext, internalCallContext);
+ return performOperation(isApiPayment, attemptId, TransactionType.PURCHASE, account, paymentMethodId, paymentId, null, amount, currency, paymentExternalKey, paymentTransactionExternalKey, paymentIdForNewPayment, paymentTransactionIdForNewPaymentTransaction, shouldLockAccountAndDispatch, null, properties, callContext, internalCallContext);
}
- public Payment createVoid(final boolean isApiPayment, @Nullable final UUID attemptId, final Account account, final UUID paymentId, @Nullable final String paymentTransactionExternalKey, final boolean shouldLockAccountAndDispatch,
+ public Payment createVoid(final boolean isApiPayment, @Nullable final UUID attemptId, final Account account, final UUID paymentId, @Nullable final String paymentTransactionExternalKey, @Nullable final UUID paymentTransactionIdForNewPaymentTransaction, final boolean shouldLockAccountAndDispatch,
final Iterable<PluginProperty> properties, final CallContext callContext, final InternalCallContext internalCallContext) throws PaymentApiException {
- return performOperation(isApiPayment, attemptId, TransactionType.VOID, account, null, paymentId, null, null, null, null, paymentTransactionExternalKey, shouldLockAccountAndDispatch, null, properties, callContext, internalCallContext);
+ return performOperation(isApiPayment, attemptId, TransactionType.VOID, account, null, paymentId, null, null, null, null, paymentTransactionExternalKey, null, paymentTransactionIdForNewPaymentTransaction, shouldLockAccountAndDispatch, null, properties, callContext, internalCallContext);
}
public Payment createRefund(final boolean isApiPayment, @Nullable final UUID attemptId, final Account account, final UUID paymentId, final BigDecimal amount, final Currency currency,
- final String paymentTransactionExternalKey, final boolean shouldLockAccountAndDispatch,
+ final String paymentTransactionExternalKey, @Nullable final UUID paymentTransactionIdForNewPaymentTransaction, final boolean shouldLockAccountAndDispatch,
final Iterable<PluginProperty> properties, final CallContext callContext, final InternalCallContext internalCallContext) throws PaymentApiException {
- return performOperation(isApiPayment, attemptId, TransactionType.REFUND, account, null, paymentId, null, amount, currency, null, paymentTransactionExternalKey, shouldLockAccountAndDispatch, null, properties, callContext, internalCallContext);
+ return performOperation(isApiPayment, attemptId, TransactionType.REFUND, account, null, paymentId, null, amount, currency, null, paymentTransactionExternalKey, null, paymentTransactionIdForNewPaymentTransaction, shouldLockAccountAndDispatch, null, properties, callContext, internalCallContext);
}
public Payment createCredit(final boolean isApiPayment, @Nullable final UUID attemptId, final Account account, @Nullable final UUID paymentMethodId, @Nullable final UUID paymentId, final BigDecimal amount, final Currency currency,
- @Nullable final String paymentExternalKey, @Nullable final String paymentTransactionExternalKey, final boolean shouldLockAccountAndDispatch,
+ @Nullable final String paymentExternalKey, @Nullable final String paymentTransactionExternalKey, @Nullable final UUID paymentIdForNewPayment, @Nullable final UUID paymentTransactionIdForNewPaymentTransaction, final boolean shouldLockAccountAndDispatch,
final Iterable<PluginProperty> properties, final CallContext callContext, final InternalCallContext internalCallContext) throws PaymentApiException {
- return performOperation(isApiPayment, attemptId, TransactionType.CREDIT, account, paymentMethodId, paymentId, null, amount, currency, paymentExternalKey, paymentTransactionExternalKey, shouldLockAccountAndDispatch, null, properties, callContext, internalCallContext);
+ return performOperation(isApiPayment, attemptId, TransactionType.CREDIT, account, paymentMethodId, paymentId, null, amount, currency, paymentExternalKey, paymentTransactionExternalKey, paymentIdForNewPayment, paymentTransactionIdForNewPaymentTransaction, shouldLockAccountAndDispatch, null, properties, callContext, internalCallContext);
}
- public Payment createChargeback(final boolean isApiPayment, @Nullable final UUID attemptId, final Account account, final UUID paymentId, @Nullable final String paymentTransactionExternalKey, final BigDecimal amount, final Currency currency, final boolean shouldLockAccountAndDispatch,
+ public Payment createChargeback(final boolean isApiPayment, @Nullable final UUID attemptId, final Account account, final UUID paymentId, @Nullable final String paymentTransactionExternalKey, final BigDecimal amount, final Currency currency, @Nullable final UUID paymentTransactionIdForNewPaymentTransaction, final boolean shouldLockAccountAndDispatch,
final CallContext callContext, final InternalCallContext internalCallContext) throws PaymentApiException {
- return performOperation(isApiPayment, attemptId, TransactionType.CHARGEBACK, account, null, paymentId, null, amount, currency, null, paymentTransactionExternalKey, shouldLockAccountAndDispatch, null, PLUGIN_PROPERTIES, callContext, internalCallContext);
+ return performOperation(isApiPayment, attemptId, TransactionType.CHARGEBACK, account, null, paymentId, null, amount, currency, null, paymentTransactionExternalKey, null, paymentTransactionIdForNewPaymentTransaction, shouldLockAccountAndDispatch, null, PLUGIN_PROPERTIES, callContext, internalCallContext);
}
- public Payment createChargebackReversal(final boolean isApiPayment, @Nullable final UUID attemptId, final Account account, final UUID paymentId, @Nullable final String paymentTransactionExternalKey, final BigDecimal amount, final Currency currency, final boolean shouldLockAccountAndDispatch,
- final CallContext callContext, final InternalCallContext internalCallContext) throws PaymentApiException {
- return performOperation(isApiPayment, attemptId, TransactionType.CHARGEBACK, account, null, paymentId, null, amount, currency, null, paymentTransactionExternalKey, shouldLockAccountAndDispatch, OperationResult.FAILURE, PLUGIN_PROPERTIES, callContext, internalCallContext);
+ public Payment createChargebackReversal(final boolean isApiPayment, @Nullable final UUID attemptId, final Account account, final UUID paymentId, @Nullable final String paymentTransactionExternalKey, final BigDecimal amount, final Currency currency, @Nullable final UUID paymentTransactionIdForNewPaymentTransaction, final boolean shouldLockAccountAndDispatch,
+ final CallContext callContext, final InternalCallContext internalCallContext) throws PaymentApiException {
+ return performOperation(isApiPayment, attemptId, TransactionType.CHARGEBACK, account, null, paymentId, null, amount, currency, null, paymentTransactionExternalKey, null, paymentTransactionIdForNewPaymentTransaction, shouldLockAccountAndDispatch, OperationResult.FAILURE, PLUGIN_PROPERTIES, callContext, internalCallContext);
}
public Payment notifyPendingPaymentOfStateChanged(final Account account, final UUID transactionId, final boolean isSuccess, final CallContext callContext, final InternalCallContext internalCallContext) throws PaymentApiException {
@@ -186,7 +184,7 @@ public class PaymentProcessor extends ProcessorBase {
final boolean runJanitor = false;
return performOperation(true, runJanitor, null, transactionModelDao.getTransactionType(), account, null, transactionModelDao.getPaymentId(),
- transactionModelDao.getId(), transactionModelDao.getAmount(), transactionModelDao.getCurrency(), null, transactionModelDao.getTransactionExternalKey(), true,
+ transactionModelDao.getId(), transactionModelDao.getAmount(), transactionModelDao.getCurrency(), null, transactionModelDao.getTransactionExternalKey(), null, null, true,
overridePluginResult, PLUGIN_PROPERTIES, callContext, internalCallContext);
}
@@ -206,7 +204,7 @@ public class PaymentProcessor extends ProcessorBase {
PaymentPluginApi pluginApi = paymentPluginByPaymentMethodId.get(paymentModelDao.getPaymentMethodId());
if (pluginApi == null && !absentPlugins.contains(paymentModelDao.getPaymentMethodId())) {
try {
- pluginApi = getPaymentProviderPlugin(paymentModelDao.getPaymentMethodId(), tenantContext);
+ pluginApi = getPaymentProviderPlugin(paymentModelDao.getPaymentMethodId(), true, tenantContext);
paymentPluginByPaymentMethodId.put(paymentModelDao.getPaymentMethodId(), pluginApi);
} catch (final PaymentApiException e) {
log.warn("Unable to retrieve pluginApi for payment method " + paymentModelDao.getPaymentMethodId());
@@ -263,7 +261,7 @@ public class PaymentProcessor extends ProcessorBase {
} else {
if (paymentMethodIdToPaymentPluginApi.get(paymentModelDao.getPaymentMethodId()) == null) {
try {
- final PaymentPluginApi paymentProviderPlugin = getPaymentProviderPlugin(paymentModelDao.getPaymentMethodId(), internalTenantContext);
+ final PaymentPluginApi paymentProviderPlugin = getPaymentProviderPlugin(paymentModelDao.getPaymentMethodId(), true, internalTenantContext);
paymentMethodIdToPaymentPluginApi.put(paymentModelDao.getPaymentMethodId(), Optional.<PaymentPluginApi>of(paymentProviderPlugin));
} catch (final PaymentApiException e) {
log.warn("Unable to retrieve PaymentPluginApi for paymentMethodId='{}'", paymentModelDao.getPaymentMethodId(), e);
@@ -336,7 +334,7 @@ public class PaymentProcessor extends ProcessorBase {
);
} catch (final PaymentApiException e) {
log.warn("Unable to search through payments", e);
- return new DefaultPagination<Payment>(offset, limit, null, null, Iterators.<Payment>emptyIterator());
+ return new DefaultPagination<Payment>(offset, limit, null, null, ImmutableSet.<Payment>of().iterator());
}
}
}
@@ -443,6 +441,8 @@ public class PaymentProcessor extends ProcessorBase {
@Nullable final Currency currency,
@Nullable final String paymentExternalKey,
@Nullable final String paymentTransactionExternalKey,
+ @Nullable final UUID paymentIdForNewPayment,
+ @Nullable final UUID paymentTransactionIdForNewPaymentTransaction,
final boolean shouldLockAccountAndDispatch,
@Nullable final OperationResult overridePluginOperationResult,
final Iterable<PluginProperty> properties,
@@ -451,7 +451,7 @@ public class PaymentProcessor extends ProcessorBase {
boolean runJanitor = true;
return performOperation(isApiPayment, runJanitor, attemptId, transactionType, account, paymentMethodId, paymentId,
transactionId, amount, currency, paymentExternalKey, paymentTransactionExternalKey,
- shouldLockAccountAndDispatch, overridePluginOperationResult, properties, callContext, internalCallContext);
+ paymentIdForNewPayment, paymentTransactionIdForNewPaymentTransaction, shouldLockAccountAndDispatch, overridePluginOperationResult, properties, callContext, internalCallContext);
}
private Payment performOperation(final boolean isApiPayment,
@@ -466,6 +466,8 @@ public class PaymentProcessor extends ProcessorBase {
@Nullable final Currency currency,
@Nullable final String paymentExternalKey,
@Nullable final String paymentTransactionExternalKey,
+ @Nullable final UUID paymentIdForNewPayment,
+ @Nullable final UUID paymentTransactionIdForNewPaymentTransaction,
final boolean shouldLockAccountAndDispatch,
@Nullable final OperationResult overridePluginOperationResult,
final Iterable<PluginProperty> properties,
@@ -482,6 +484,8 @@ public class PaymentProcessor extends ProcessorBase {
paymentTransactionExternalKey,
amount,
currency,
+ paymentIdForNewPayment,
+ paymentTransactionIdForNewPaymentTransaction,
shouldLockAccountAndDispatch,
overridePluginOperationResult,
properties,
@@ -503,7 +507,7 @@ public class PaymentProcessor extends ProcessorBase {
// Always invoke the Janitor first to get the latest state. The state machine will then
// prevent disallowed transitions in case the state couldn't be fixed (or if it's already in a final state).
if (runJanitor) {
- final PaymentPluginApi plugin = getPaymentProviderPlugin(paymentModelDao.getPaymentMethodId(), internalCallContext);
+ final PaymentPluginApi plugin = getPaymentProviderPlugin(paymentModelDao.getPaymentMethodId(), true, internalCallContext);
final List<PaymentTransactionInfoPlugin> pluginTransactions = getPaymentTransactionInfoPlugins(plugin, paymentModelDao, properties, callContext);
paymentModelDao = invokeJanitor(paymentModelDao, paymentTransactionsForCurrentPayment, pluginTransactions, internalCallContext);
}
@@ -642,7 +646,7 @@ public class PaymentProcessor extends ProcessorBase {
// Used in single get APIs (getPayment / getPaymentByExternalKey)
private Payment toPayment(final PaymentModelDao paymentModelDao, final boolean withPluginInfo, final boolean withAttempts, final Iterable<PluginProperty> properties, final TenantContext context, final InternalTenantContext tenantContext) throws PaymentApiException {
- final PaymentPluginApi plugin = getPaymentProviderPlugin(paymentModelDao.getPaymentMethodId(), tenantContext);
+ final PaymentPluginApi plugin = getPaymentProviderPlugin(paymentModelDao.getPaymentMethodId(), true, tenantContext);
final List<PaymentTransactionInfoPlugin> pluginTransactions = withPluginInfo ? getPaymentTransactionInfoPlugins(plugin, paymentModelDao, properties, context) : null;
return toPayment(paymentModelDao, pluginTransactions, withAttempts, tenantContext);
diff --git a/payment/src/main/java/org/killbill/billing/payment/core/PluginControlPaymentProcessor.java b/payment/src/main/java/org/killbill/billing/payment/core/PluginControlPaymentProcessor.java
index 736fa84..9491e51 100644
--- a/payment/src/main/java/org/killbill/billing/payment/core/PluginControlPaymentProcessor.java
+++ b/payment/src/main/java/org/killbill/billing/payment/core/PluginControlPaymentProcessor.java
@@ -1,6 +1,6 @@
/*
- * Copyright 2014-2016 Groupon, Inc
- * Copyright 2014-2016 The Billing Project, LLC
+ * Copyright 2014-2017 Groupon, Inc
+ * Copyright 2014-2017 The Billing Project, LLC
*
* The Billing Project licenses this file to you under the Apache License, version 2.0
* (the "License"); you may not use this file except in compliance with the
@@ -34,7 +34,6 @@ import org.killbill.billing.account.api.AccountInternalApi;
import org.killbill.billing.callcontext.InternalCallContext;
import org.killbill.billing.catalog.api.Currency;
import org.killbill.billing.invoice.api.InvoiceInternalApi;
-import org.killbill.billing.osgi.api.OSGIServiceRegistration;
import org.killbill.billing.payment.api.Payment;
import org.killbill.billing.payment.api.PaymentApiException;
import org.killbill.billing.payment.api.PaymentTransaction;
@@ -50,7 +49,6 @@ import org.killbill.billing.payment.dao.PaymentTransactionModelDao;
import org.killbill.billing.payment.dao.PluginPropertySerializer;
import org.killbill.billing.payment.dao.PluginPropertySerializer.PluginPropertySerializerException;
import org.killbill.billing.payment.invoice.InvoicePaymentControlPluginApi;
-import org.killbill.billing.payment.plugin.api.PaymentPluginApi;
import org.killbill.billing.tag.TagInternalApi;
import org.killbill.billing.util.callcontext.CallContext;
import org.killbill.billing.util.callcontext.InternalCallContextFactory;
@@ -78,7 +76,7 @@ public class PluginControlPaymentProcessor extends ProcessorBase {
private final PaymentControlStateMachineHelper paymentControlStateMachineHelper;
@Inject
- public PluginControlPaymentProcessor(final OSGIServiceRegistration<PaymentPluginApi> pluginRegistry,
+ public PluginControlPaymentProcessor(final PaymentPluginServiceRegistration paymentPluginServiceRegistration,
final AccountInternalApi accountInternalApi,
final InvoiceInternalApi invoiceApi,
final TagInternalApi tagUserApi,
@@ -88,7 +86,7 @@ public class PluginControlPaymentProcessor extends ProcessorBase {
final PluginControlPaymentAutomatonRunner pluginControlledPaymentAutomatonRunner,
final PaymentControlStateMachineHelper paymentControlStateMachineHelper,
final Clock clock) {
- super(pluginRegistry, accountInternalApi, paymentDao, tagUserApi, locker, internalCallContextFactory, invoiceApi, clock);
+ super(paymentPluginServiceRegistration, accountInternalApi, paymentDao, tagUserApi, locker, internalCallContextFactory, invoiceApi, clock);
this.paymentControlStateMachineHelper = paymentControlStateMachineHelper;
this.pluginControlledPaymentAutomatonRunner = pluginControlledPaymentAutomatonRunner;
}
diff --git a/payment/src/main/java/org/killbill/billing/payment/core/ProcessorBase.java b/payment/src/main/java/org/killbill/billing/payment/core/ProcessorBase.java
index 5866bcc..32c6cab 100644
--- a/payment/src/main/java/org/killbill/billing/payment/core/ProcessorBase.java
+++ b/payment/src/main/java/org/killbill/billing/payment/core/ProcessorBase.java
@@ -1,7 +1,7 @@
/*
* Copyright 2010-2013 Ning, Inc.
- * Copyright 2014-2015 Groupon, Inc
- * Copyright 2014-2015 The Billing Project, LLC
+ * Copyright 2014-2017 Groupon, Inc
+ * Copyright 2014-2017 The Billing Project, LLC
*
* The Billing Project licenses this file to you under the Apache License, version 2.0
* (the "License"); you may not use this file except in compliance with the
@@ -27,12 +27,10 @@ import javax.annotation.Nullable;
import org.killbill.billing.ErrorCode;
import org.killbill.billing.ObjectType;
-import org.killbill.billing.account.api.Account;
import org.killbill.billing.account.api.AccountInternalApi;
import org.killbill.billing.callcontext.InternalCallContext;
import org.killbill.billing.callcontext.InternalTenantContext;
import org.killbill.billing.invoice.api.InvoiceInternalApi;
-import org.killbill.billing.osgi.api.OSGIServiceRegistration;
import org.killbill.billing.payment.api.PaymentApiException;
import org.killbill.billing.payment.dao.PaymentDao;
import org.killbill.billing.payment.dao.PaymentMethodModelDao;
@@ -61,7 +59,8 @@ public abstract class ProcessorBase {
private static final Logger log = LoggerFactory.getLogger(ProcessorBase.class);
- protected final OSGIServiceRegistration<PaymentPluginApi> pluginRegistry;
+ private final PaymentPluginServiceRegistration paymentPluginServiceRegistration;
+
protected final AccountInternalApi accountInternalApi;
protected final GlobalLocker locker;
protected final PaymentDao paymentDao;
@@ -70,7 +69,7 @@ public abstract class ProcessorBase {
protected final Clock clock;
protected final InvoiceInternalApi invoiceApi;
- public ProcessorBase(final OSGIServiceRegistration<PaymentPluginApi> pluginRegistry,
+ public ProcessorBase(final PaymentPluginServiceRegistration paymentPluginServiceRegistration,
final AccountInternalApi accountInternalApi,
final PaymentDao paymentDao,
final TagInternalApi tagInternalApi,
@@ -78,7 +77,7 @@ public abstract class ProcessorBase {
final InternalCallContextFactory internalCallContextFactory,
final InvoiceInternalApi invoiceApi,
final Clock clock) {
- this.pluginRegistry = pluginRegistry;
+ this.paymentPluginServiceRegistration = paymentPluginServiceRegistration;
this.accountInternalApi = accountInternalApi;
this.paymentDao = paymentDao;
this.locker = locker;
@@ -110,42 +109,23 @@ public abstract class ProcessorBase {
}
public Set<String> getAvailablePlugins() {
- return pluginRegistry.getAllServices();
+ return paymentPluginServiceRegistration.getAvailablePlugins();
}
protected PaymentPluginApi getPaymentPluginApi(final String pluginName) throws PaymentApiException {
- final PaymentPluginApi pluginApi = pluginRegistry.getServiceForName(pluginName);
- if (pluginApi == null) {
- throw new PaymentApiException(ErrorCode.PAYMENT_NO_SUCH_PAYMENT_PLUGIN, pluginName);
- }
- return pluginApi;
- }
-
- protected PaymentPluginApi getPaymentProviderPlugin(final UUID paymentMethodId, final InternalTenantContext context) throws PaymentApiException {
- final String pluginName = getPaymentProviderPluginName(paymentMethodId, context);
- return getPaymentPluginApi(pluginName);
+ return paymentPluginServiceRegistration.getPaymentPluginApi(pluginName);
}
- protected String getPaymentProviderPluginName(final UUID paymentMethodId, final InternalTenantContext context) throws PaymentApiException {
- final PaymentMethodModelDao methodDao = paymentDao.getPaymentMethodIncludedDeleted(paymentMethodId, context);
- if (methodDao == null) {
- log.error("PaymentMethod does not exist", paymentMethodId);
- throw new PaymentApiException(ErrorCode.PAYMENT_NO_SUCH_PAYMENT_METHOD, paymentMethodId);
- }
- return methodDao.getPluginName();
+ protected PaymentPluginApi getPaymentProviderPlugin(final UUID paymentMethodId, final boolean includedDeleted, final InternalTenantContext context) throws PaymentApiException {
+ return paymentPluginServiceRegistration.getPaymentPluginApi(paymentMethodId, includedDeleted, context);
}
- protected PaymentPluginApi getPaymentProviderPlugin(final Account account, final InternalTenantContext context) throws PaymentApiException {
- final UUID paymentMethodId = getDefaultPaymentMethodId(account);
- return getPaymentProviderPlugin(paymentMethodId, context);
+ protected PaymentMethodModelDao getPaymentMethodById(final UUID paymentMethodId, final boolean includedDeleted, final InternalTenantContext context) throws PaymentApiException {
+ return paymentPluginServiceRegistration.getPaymentMethodById(paymentMethodId, includedDeleted, context);
}
- protected UUID getDefaultPaymentMethodId(final Account account) throws PaymentApiException {
- final UUID paymentMethodId = account.getPaymentMethodId();
- if (paymentMethodId == null) {
- throw new PaymentApiException(ErrorCode.PAYMENT_NO_DEFAULT_PAYMENT_METHOD, account.getId());
- }
- return paymentMethodId;
+ protected PaymentMethodModelDao getPaymentMethodByExternalKey(final String paymentMethodExternalKey, final boolean includedDeleted, final InternalTenantContext context) throws PaymentApiException {
+ return paymentPluginServiceRegistration.getPaymentMethodByExternalKey(paymentMethodExternalKey, includedDeleted, context);
}
protected TenantContext buildTenantContext(final InternalTenantContext context) {
diff --git a/payment/src/main/java/org/killbill/billing/payment/core/sm/control/AuthorizeControlOperation.java b/payment/src/main/java/org/killbill/billing/payment/core/sm/control/AuthorizeControlOperation.java
index 8da4e82..93ce8b7 100644
--- a/payment/src/main/java/org/killbill/billing/payment/core/sm/control/AuthorizeControlOperation.java
+++ b/payment/src/main/java/org/killbill/billing/payment/core/sm/control/AuthorizeControlOperation.java
@@ -46,6 +46,8 @@ public class AuthorizeControlOperation extends OperationControlCallback {
paymentStateControlContext.getCurrency(),
paymentStateControlContext.getPaymentExternalKey(),
paymentStateControlContext.getPaymentTransactionExternalKey(),
+ paymentStateControlContext.getPaymentIdForNewPayment(),
+ paymentStateControlContext.getPaymentTransactionIdForNewPaymentTransaction(),
false,
paymentStateControlContext.getProperties(),
paymentStateControlContext.getCallContext(),
diff --git a/payment/src/main/java/org/killbill/billing/payment/core/sm/control/CaptureControlOperation.java b/payment/src/main/java/org/killbill/billing/payment/core/sm/control/CaptureControlOperation.java
index 36776b3..af79455 100644
--- a/payment/src/main/java/org/killbill/billing/payment/core/sm/control/CaptureControlOperation.java
+++ b/payment/src/main/java/org/killbill/billing/payment/core/sm/control/CaptureControlOperation.java
@@ -44,6 +44,7 @@ public class CaptureControlOperation extends OperationControlCallback {
paymentStateControlContext.getAmount(),
paymentStateControlContext.getCurrency(),
paymentStateControlContext.getPaymentTransactionExternalKey(),
+ paymentStateControlContext.getPaymentTransactionIdForNewPaymentTransaction(),
false,
paymentStateControlContext.getProperties(),
paymentStateControlContext.getCallContext(),
diff --git a/payment/src/main/java/org/killbill/billing/payment/core/sm/control/ChargebackControlOperation.java b/payment/src/main/java/org/killbill/billing/payment/core/sm/control/ChargebackControlOperation.java
index 99e6a97..d53481f 100644
--- a/payment/src/main/java/org/killbill/billing/payment/core/sm/control/ChargebackControlOperation.java
+++ b/payment/src/main/java/org/killbill/billing/payment/core/sm/control/ChargebackControlOperation.java
@@ -45,6 +45,7 @@ public class ChargebackControlOperation extends OperationControlCallback {
paymentStateControlContext.getPaymentTransactionExternalKey(),
paymentStateControlContext.getAmount(),
paymentStateControlContext.getCurrency(),
+ paymentStateControlContext.getPaymentTransactionIdForNewPaymentTransaction(),
false,
paymentStateControlContext.getCallContext(),
paymentStateControlContext.getInternalCallContext());
diff --git a/payment/src/main/java/org/killbill/billing/payment/core/sm/control/ChargebackReversalControlOperation.java b/payment/src/main/java/org/killbill/billing/payment/core/sm/control/ChargebackReversalControlOperation.java
index a0af618..d258b1e 100644
--- a/payment/src/main/java/org/killbill/billing/payment/core/sm/control/ChargebackReversalControlOperation.java
+++ b/payment/src/main/java/org/killbill/billing/payment/core/sm/control/ChargebackReversalControlOperation.java
@@ -45,6 +45,7 @@ public class ChargebackReversalControlOperation extends OperationControlCallback
paymentStateControlContext.getPaymentTransactionExternalKey(),
paymentStateControlContext.getAmount(),
paymentStateControlContext.getCurrency(),
+ paymentStateControlContext.getPaymentTransactionIdForNewPaymentTransaction(),
false,
paymentStateControlContext.getCallContext(),
paymentStateControlContext.getInternalCallContext());
diff --git a/payment/src/main/java/org/killbill/billing/payment/core/sm/control/ControlPluginRunner.java b/payment/src/main/java/org/killbill/billing/payment/core/sm/control/ControlPluginRunner.java
index d212925..bb1bbb6 100644
--- a/payment/src/main/java/org/killbill/billing/payment/core/sm/control/ControlPluginRunner.java
+++ b/payment/src/main/java/org/killbill/billing/payment/core/sm/control/ControlPluginRunner.java
@@ -110,6 +110,11 @@ public class ControlPluginRunner {
log.debug("Calling priorCall of plugin {}", pluginName);
prevResult = plugin.priorCall(inputPaymentControlContext, inputPluginProperties);
log.debug("Successful executed priorCall of plugin {}", pluginName);
+ if (prevResult == null) {
+ // Nothing returned by the plugin
+ continue;
+ }
+
if (prevResult.getAdjustedPaymentMethodId() != null) {
inputPaymentMethodId = prevResult.getAdjustedPaymentMethodId();
}
@@ -143,7 +148,7 @@ public class ControlPluginRunner {
callContext);
}
// Rebuild latest result to include inputPluginProperties
- prevResult = new DefaultPriorPaymentControlResult(prevResult.isAborted(), inputPaymentMethodId, inputAmount, inputCurrency, inputPluginProperties);
+ prevResult = new DefaultPriorPaymentControlResult(prevResult != null && prevResult.isAborted(), inputPaymentMethodId, inputAmount, inputCurrency, inputPluginProperties);
return prevResult;
}
@@ -191,6 +196,11 @@ public class ControlPluginRunner {
log.debug("Calling onSuccessCall of plugin {}", pluginName);
final OnSuccessPaymentControlResult result = plugin.onSuccessCall(inputPaymentControlContext, inputPluginProperties);
log.debug("Successful executed onSuccessCall of plugin {}", pluginName);
+ if (result == null) {
+ // Nothing returned by the plugin
+ continue;
+ }
+
if (result.getAdjustedPluginProperties() != null) {
inputPluginProperties = result.getAdjustedPluginProperties();
}
@@ -251,6 +261,11 @@ public class ControlPluginRunner {
log.debug("Calling onSuccessCall of plugin {}", pluginName);
final OnFailurePaymentControlResult result = plugin.onFailureCall(inputPaymentControlContext, inputPluginProperties);
log.debug("Successful executed onSuccessCall of plugin {}", pluginName);
+ if (result == null) {
+ // Nothing returned by the plugin
+ continue;
+ }
+
if (candidate == null) {
candidate = result.getNextRetryDate();
} else if (result.getNextRetryDate() != null) {
diff --git a/payment/src/main/java/org/killbill/billing/payment/core/sm/control/CreditControlOperation.java b/payment/src/main/java/org/killbill/billing/payment/core/sm/control/CreditControlOperation.java
index 9e91396..e8c3e91 100644
--- a/payment/src/main/java/org/killbill/billing/payment/core/sm/control/CreditControlOperation.java
+++ b/payment/src/main/java/org/killbill/billing/payment/core/sm/control/CreditControlOperation.java
@@ -46,6 +46,8 @@ public class CreditControlOperation extends OperationControlCallback {
paymentStateControlContext.getCurrency(),
paymentStateControlContext.getPaymentExternalKey(),
paymentStateControlContext.getPaymentTransactionExternalKey(),
+ paymentStateControlContext.getPaymentIdForNewPayment(),
+ paymentStateControlContext.getPaymentTransactionIdForNewPaymentTransaction(),
false,
paymentStateControlContext.getProperties(),
paymentStateControlContext.getCallContext(),
diff --git a/payment/src/main/java/org/killbill/billing/payment/core/sm/control/DefaultControlInitiated.java b/payment/src/main/java/org/killbill/billing/payment/core/sm/control/DefaultControlInitiated.java
index 4b35883..cbf4136 100644
--- a/payment/src/main/java/org/killbill/billing/payment/core/sm/control/DefaultControlInitiated.java
+++ b/payment/src/main/java/org/killbill/billing/payment/core/sm/control/DefaultControlInitiated.java
@@ -18,6 +18,7 @@
package org.killbill.billing.payment.core.sm.control;
import java.util.List;
+import java.util.UUID;
import org.joda.time.DateTime;
import org.killbill.automaton.OperationException;
@@ -85,7 +86,9 @@ public class DefaultControlInitiated implements LeavingStateCallback {
stateContext.setPaymentExternalKey(payment.getExternalKey());
stateContext.setPaymentMethodId(payment.getPaymentMethodId());
} else if (stateContext.getPaymentExternalKey() == null) {
- stateContext.setPaymentExternalKey(UUIDs.randomUUID().toString());
+ final UUID paymentIdForNewPayment = UUIDs.randomUUID();
+ stateContext.setPaymentIdForNewPayment(paymentIdForNewPayment);
+ stateContext.setPaymentExternalKey(paymentIdForNewPayment.toString());
}
if (paymentTransactionModelDao != null) {
@@ -93,7 +96,9 @@ public class DefaultControlInitiated implements LeavingStateCallback {
stateContext.setProcessedAmount(paymentTransactionModelDao.getProcessedAmount());
stateContext.setProcessedCurrency(paymentTransactionModelDao.getProcessedCurrency());
} else if (stateContext.getPaymentTransactionExternalKey() == null) {
- stateContext.setPaymentTransactionExternalKey(UUIDs.randomUUID().toString());
+ final UUID paymentTransactionIdForNewPaymentTransaction = UUIDs.randomUUID();
+ stateContext.setPaymentTransactionIdForNewPaymentTransaction(paymentTransactionIdForNewPaymentTransaction);
+ stateContext.setPaymentTransactionExternalKey(paymentTransactionIdForNewPaymentTransaction.toString());
}
if (stateContext.getPaymentMethodId() == null) {
diff --git a/payment/src/main/java/org/killbill/billing/payment/core/sm/control/PaymentStateControlContext.java b/payment/src/main/java/org/killbill/billing/payment/core/sm/control/PaymentStateControlContext.java
index 920c660..599e653 100644
--- a/payment/src/main/java/org/killbill/billing/payment/core/sm/control/PaymentStateControlContext.java
+++ b/payment/src/main/java/org/killbill/billing/payment/core/sm/control/PaymentStateControlContext.java
@@ -61,11 +61,19 @@ public class PaymentStateControlContext extends PaymentStateContext {
final BigDecimal amount,
final Currency currency,
final Iterable<PluginProperty> properties, final InternalCallContext internalCallContext, final CallContext callContext) {
- super(isApiPayment, paymentId, transactionId, null, paymentExternalKey, paymentTransactionExternalKey, transactionType, account, paymentMethodId, amount, currency, true, null, properties, internalCallContext, callContext);
+ super(isApiPayment, paymentId, transactionId, null, paymentExternalKey, paymentTransactionExternalKey, transactionType, account, paymentMethodId, amount, currency, null, null, true, null, properties, internalCallContext, callContext);
this.paymentControlPluginNames = paymentControlPluginNames;
this.isSuccess = isSuccess;
}
+ public void setPaymentIdForNewPayment(final UUID paymentIdForNewPayment) {
+ this.paymentIdForNewPayment = paymentIdForNewPayment;
+ }
+
+ public void setPaymentTransactionIdForNewPaymentTransaction(final UUID paymentTransactionIdForNewPaymentTransaction) {
+ this.paymentTransactionIdForNewPaymentTransaction = paymentTransactionIdForNewPaymentTransaction;
+ }
+
public DateTime getRetryDate() {
return retryDate;
}
diff --git a/payment/src/main/java/org/killbill/billing/payment/core/sm/control/PurchaseControlOperation.java b/payment/src/main/java/org/killbill/billing/payment/core/sm/control/PurchaseControlOperation.java
index 401b780..cacc797 100644
--- a/payment/src/main/java/org/killbill/billing/payment/core/sm/control/PurchaseControlOperation.java
+++ b/payment/src/main/java/org/killbill/billing/payment/core/sm/control/PurchaseControlOperation.java
@@ -46,6 +46,8 @@ public class PurchaseControlOperation extends OperationControlCallback {
paymentStateControlContext.getCurrency(),
paymentStateControlContext.getPaymentExternalKey(),
paymentStateControlContext.getPaymentTransactionExternalKey(),
+ paymentStateControlContext.getPaymentIdForNewPayment(),
+ paymentStateControlContext.getPaymentTransactionIdForNewPaymentTransaction(),
false,
paymentStateControlContext.getProperties(),
paymentStateControlContext.getCallContext(),
diff --git a/payment/src/main/java/org/killbill/billing/payment/core/sm/control/RefundControlOperation.java b/payment/src/main/java/org/killbill/billing/payment/core/sm/control/RefundControlOperation.java
index 64fda3f..8ed65db 100644
--- a/payment/src/main/java/org/killbill/billing/payment/core/sm/control/RefundControlOperation.java
+++ b/payment/src/main/java/org/killbill/billing/payment/core/sm/control/RefundControlOperation.java
@@ -44,6 +44,7 @@ public class RefundControlOperation extends OperationControlCallback {
paymentStateControlContext.getAmount(),
paymentStateControlContext.getCurrency(),
paymentStateControlContext.getPaymentTransactionExternalKey(),
+ paymentStateControlContext.getPaymentTransactionIdForNewPaymentTransaction(),
false,
paymentStateControlContext.getProperties(),
paymentStateControlContext.getCallContext(),
diff --git a/payment/src/main/java/org/killbill/billing/payment/core/sm/control/VoidControlOperation.java b/payment/src/main/java/org/killbill/billing/payment/core/sm/control/VoidControlOperation.java
index f600e8b..9433a56 100644
--- a/payment/src/main/java/org/killbill/billing/payment/core/sm/control/VoidControlOperation.java
+++ b/payment/src/main/java/org/killbill/billing/payment/core/sm/control/VoidControlOperation.java
@@ -42,6 +42,7 @@ public class VoidControlOperation extends OperationControlCallback {
paymentStateControlContext.getAccount(),
paymentStateControlContext.getPaymentId(),
paymentStateControlContext.getPaymentTransactionExternalKey(),
+ paymentStateControlContext.getPaymentTransactionIdForNewPaymentTransaction(),
false,
paymentStateControlContext.getProperties(),
paymentStateControlContext.getCallContext(),
diff --git a/payment/src/main/java/org/killbill/billing/payment/core/sm/PaymentAutomatonDAOHelper.java b/payment/src/main/java/org/killbill/billing/payment/core/sm/PaymentAutomatonDAOHelper.java
index c546b0c..5b11b15 100644
--- a/payment/src/main/java/org/killbill/billing/payment/core/sm/PaymentAutomatonDAOHelper.java
+++ b/payment/src/main/java/org/killbill/billing/payment/core/sm/PaymentAutomatonDAOHelper.java
@@ -1,6 +1,6 @@
/*
- * Copyright 2014 Groupon, Inc
- * Copyright 2014 The Billing Project, LLC
+ * Copyright 2014-2017 Groupon, Inc
+ * Copyright 2014-2017 The Billing Project, LLC
*
* Groupon licenses this file to you under the Apache License, version 2.0
* (the "License"); you may not use this file except in compliance with the
@@ -27,10 +27,10 @@ import org.joda.time.DateTime;
import org.killbill.billing.ErrorCode;
import org.killbill.billing.callcontext.InternalCallContext;
import org.killbill.billing.catalog.api.Currency;
-import org.killbill.billing.osgi.api.OSGIServiceRegistration;
import org.killbill.billing.payment.api.PaymentApiException;
import org.killbill.billing.payment.api.TransactionStatus;
import org.killbill.billing.payment.api.TransactionType;
+import org.killbill.billing.payment.core.PaymentPluginServiceRegistration;
import org.killbill.billing.payment.dao.PaymentDao;
import org.killbill.billing.payment.dao.PaymentMethodModelDao;
import org.killbill.billing.payment.dao.PaymentModelDao;
@@ -54,7 +54,7 @@ public class PaymentAutomatonDAOHelper {
protected final PaymentDao paymentDao;
- private final OSGIServiceRegistration<PaymentPluginApi> pluginRegistry;
+ private final PaymentPluginServiceRegistration paymentPluginServiceRegistration;
private final PersistentBus eventBus;
// Cached
@@ -64,14 +64,14 @@ public class PaymentAutomatonDAOHelper {
// Used to build new payments and transactions
public PaymentAutomatonDAOHelper(final PaymentStateContext paymentStateContext,
final DateTime utcNow, final PaymentDao paymentDao,
- final OSGIServiceRegistration<PaymentPluginApi> pluginRegistry,
+ final PaymentPluginServiceRegistration paymentPluginServiceRegistration,
final InternalCallContext internalCallContext,
final PersistentBus eventBus,
final PaymentStateMachineHelper paymentSMHelper) throws PaymentApiException {
this.paymentStateContext = paymentStateContext;
this.utcNow = utcNow;
this.paymentDao = paymentDao;
- this.pluginRegistry = pluginRegistry;
+ this.paymentPluginServiceRegistration = paymentPluginServiceRegistration;
this.internalCallContext = internalCallContext;
this.eventBus = eventBus;
this.paymentSMHelper = paymentSMHelper;
@@ -149,18 +149,14 @@ public class PaymentAutomatonDAOHelper {
paymentStateContext.setPaymentTransactionModelDao(paymentDao.getPaymentTransaction(paymentStateContext.getPaymentTransactionModelDao().getId(), internalCallContext));
}
- public String getPaymentProviderPluginName(final boolean includeDeteled) throws PaymentApiException {
+ public String getPaymentProviderPluginName(final boolean includeDeleted) throws PaymentApiException {
if (pluginName != null) {
return pluginName;
}
- final UUID paymentMethodId = paymentStateContext.getPaymentMethodId();
- final PaymentMethodModelDao methodDao = (includeDeteled) ? paymentDao.getPaymentMethodIncludedDeleted(paymentMethodId, internalCallContext) :
- paymentDao.getPaymentMethod(paymentMethodId, internalCallContext);
- if (methodDao == null) {
- throw new PaymentApiException(ErrorCode.PAYMENT_NO_SUCH_PAYMENT_METHOD, paymentMethodId);
- }
+ final PaymentMethodModelDao methodDao = paymentPluginServiceRegistration.getPaymentMethodById(paymentStateContext.getPaymentMethodId(), includeDeleted, internalCallContext);
pluginName = methodDao.getPluginName();
+
return pluginName;
}
@@ -191,10 +187,7 @@ public class PaymentAutomatonDAOHelper {
return paymentPluginApi;
}
- paymentPluginApi = pluginRegistry.getServiceForName(pluginName);
- if (paymentPluginApi == null) {
- throw new PaymentApiException(ErrorCode.PAYMENT_NO_SUCH_PAYMENT_PLUGIN, pluginName);
- }
+ paymentPluginApi = paymentPluginServiceRegistration.getPaymentPluginApi(pluginName);
return paymentPluginApi;
}
@@ -202,11 +195,20 @@ public class PaymentAutomatonDAOHelper {
final DateTime createdDate = utcNow;
final DateTime updatedDate = utcNow;
- return new PaymentModelDao(createdDate,
- updatedDate,
- paymentStateContext.getAccount().getId(),
- paymentStateContext.getPaymentMethodId(),
- paymentStateContext.getPaymentExternalKey());
+ if (paymentStateContext.getPaymentIdForNewPayment() != null) {
+ return new PaymentModelDao(paymentStateContext.getPaymentIdForNewPayment(),
+ createdDate,
+ updatedDate,
+ paymentStateContext.getAccount().getId(),
+ paymentStateContext.getPaymentMethodId(),
+ paymentStateContext.getPaymentExternalKey());
+ } else {
+ return new PaymentModelDao(createdDate,
+ updatedDate,
+ paymentStateContext.getAccount().getId(),
+ paymentStateContext.getPaymentMethodId(),
+ paymentStateContext.getPaymentExternalKey());
+ }
}
private PaymentTransactionModelDao buildNewPaymentTransactionModelDao(final UUID paymentId) {
@@ -216,18 +218,34 @@ public class PaymentAutomatonDAOHelper {
final String gatewayErrorCode = null;
final String gatewayErrorMsg = null;
- return new PaymentTransactionModelDao(createdDate,
- updatedDate,
- paymentStateContext.getAttemptId(),
- paymentStateContext.getPaymentTransactionExternalKey(),
- paymentId,
- paymentStateContext.getTransactionType(),
- effectiveDate,
- TransactionStatus.UNKNOWN,
- paymentStateContext.getAmount(),
- paymentStateContext.getCurrency(),
- gatewayErrorCode,
- gatewayErrorMsg);
+ if (paymentStateContext.getPaymentTransactionIdForNewPaymentTransaction() != null) {
+ return new PaymentTransactionModelDao(paymentStateContext.getPaymentTransactionIdForNewPaymentTransaction(),
+ createdDate,
+ updatedDate,
+ paymentStateContext.getAttemptId(),
+ paymentStateContext.getPaymentTransactionExternalKey(),
+ paymentId,
+ paymentStateContext.getTransactionType(),
+ effectiveDate,
+ TransactionStatus.UNKNOWN,
+ paymentStateContext.getAmount(),
+ paymentStateContext.getCurrency(),
+ gatewayErrorCode,
+ gatewayErrorMsg);
+ } else {
+ return new PaymentTransactionModelDao(createdDate,
+ updatedDate,
+ paymentStateContext.getAttemptId(),
+ paymentStateContext.getPaymentTransactionExternalKey(),
+ paymentId,
+ paymentStateContext.getTransactionType(),
+ effectiveDate,
+ TransactionStatus.UNKNOWN,
+ paymentStateContext.getAmount(),
+ paymentStateContext.getCurrency(),
+ gatewayErrorCode,
+ gatewayErrorMsg);
+ }
}
public String getPluginName() {
diff --git a/payment/src/main/java/org/killbill/billing/payment/core/sm/PaymentAutomatonRunner.java b/payment/src/main/java/org/killbill/billing/payment/core/sm/PaymentAutomatonRunner.java
index 2a238a5..331d317 100644
--- a/payment/src/main/java/org/killbill/billing/payment/core/sm/PaymentAutomatonRunner.java
+++ b/payment/src/main/java/org/killbill/billing/payment/core/sm/PaymentAutomatonRunner.java
@@ -1,6 +1,6 @@
/*
- * Copyright 2014-2016 Groupon, Inc
- * Copyright 2014-2016 The Billing Project, LLC
+ * Copyright 2014-2017 Groupon, Inc
+ * Copyright 2014-2017 The Billing Project, LLC
*
* The Billing Project licenses this file to you under the Apache License, version 2.0
* (the "License"); you may not use this file except in compliance with the
@@ -40,12 +40,12 @@ import org.killbill.billing.ErrorCode;
import org.killbill.billing.account.api.Account;
import org.killbill.billing.callcontext.InternalCallContext;
import org.killbill.billing.catalog.api.Currency;
-import org.killbill.billing.osgi.api.OSGIServiceRegistration;
import org.killbill.billing.payment.api.PaymentApiException;
import org.killbill.billing.payment.api.PluginProperty;
import org.killbill.billing.payment.api.TransactionStatus;
import org.killbill.billing.payment.api.TransactionType;
import org.killbill.billing.payment.core.PaymentExecutors;
+import org.killbill.billing.payment.core.PaymentPluginServiceRegistration;
import org.killbill.billing.payment.core.sm.payments.AuthorizeCompleted;
import org.killbill.billing.payment.core.sm.payments.AuthorizeInitiated;
import org.killbill.billing.payment.core.sm.payments.AuthorizeOperation;
@@ -71,7 +71,6 @@ import org.killbill.billing.payment.dao.PaymentDao;
import org.killbill.billing.payment.dao.PaymentModelDao;
import org.killbill.billing.payment.dao.PaymentTransactionModelDao;
import org.killbill.billing.payment.dispatcher.PluginDispatcher;
-import org.killbill.billing.payment.plugin.api.PaymentPluginApi;
import org.killbill.billing.util.callcontext.CallContext;
import org.killbill.billing.util.config.definition.PaymentConfig;
import org.killbill.bus.api.PersistentBus;
@@ -86,7 +85,7 @@ public class PaymentAutomatonRunner {
protected final PaymentDao paymentDao;
protected final GlobalLocker locker;
protected final PluginDispatcher<OperationResult> paymentPluginDispatcher;
- protected final OSGIServiceRegistration<PaymentPluginApi> pluginRegistry;
+ protected final PaymentPluginServiceRegistration paymentPluginServiceRegistration;
protected final Clock clock;
private final PersistentBus eventBus;
@@ -96,7 +95,7 @@ public class PaymentAutomatonRunner {
public PaymentAutomatonRunner(final PaymentConfig paymentConfig,
final PaymentDao paymentDao,
final GlobalLocker locker,
- final OSGIServiceRegistration<PaymentPluginApi> pluginRegistry,
+ final PaymentPluginServiceRegistration paymentPluginServiceRegistration,
final Clock clock,
final PaymentExecutors executors,
final PersistentBus eventBus,
@@ -104,7 +103,7 @@ public class PaymentAutomatonRunner {
this.paymentSMHelper = paymentSMHelper;
this.paymentDao = paymentDao;
this.locker = locker;
- this.pluginRegistry = pluginRegistry;
+ this.paymentPluginServiceRegistration = paymentPluginServiceRegistration;
this.clock = clock;
this.eventBus = eventBus;
this.paymentConfig = paymentConfig;
@@ -123,6 +122,8 @@ public class PaymentAutomatonRunner {
final String paymentTransactionExternalKey,
@Nullable final BigDecimal amount,
@Nullable final Currency currency,
+ @Nullable final UUID paymentIdForNewPayment,
+ @Nullable final UUID paymentTransactionIdForNewPaymentTransaction,
final boolean shouldLockAccount,
final OperationResult overridePluginOperationResult,
final Iterable<PluginProperty> properties,
@@ -143,6 +144,8 @@ public class PaymentAutomatonRunner {
paymentMethodId,
amount,
currency,
+ paymentIdForNewPayment,
+ paymentTransactionIdForNewPaymentTransaction,
shouldLockAccount,
overridePluginOperationResult,
properties,
@@ -154,7 +157,7 @@ public class PaymentAutomatonRunner {
final InternalCallContext internalCallContext) throws PaymentApiException {
final DateTime utcNow = clock.getUTCNow();
- return new PaymentAutomatonDAOHelper(paymentStateContext, utcNow, paymentDao, pluginRegistry, internalCallContext, eventBus, paymentSMHelper);
+ return new PaymentAutomatonDAOHelper(paymentStateContext, utcNow, paymentDao, paymentPluginServiceRegistration, internalCallContext, eventBus, paymentSMHelper);
}
public UUID run(final PaymentStateContext paymentStateContext,
diff --git a/payment/src/main/java/org/killbill/billing/payment/core/sm/PaymentStateContext.java b/payment/src/main/java/org/killbill/billing/payment/core/sm/PaymentStateContext.java
index 8975b22..632f15d 100644
--- a/payment/src/main/java/org/killbill/billing/payment/core/sm/PaymentStateContext.java
+++ b/payment/src/main/java/org/killbill/billing/payment/core/sm/PaymentStateContext.java
@@ -33,6 +33,7 @@ import org.killbill.billing.payment.dao.PaymentTransactionModelDao;
import org.killbill.billing.payment.plugin.api.PaymentTransactionInfoPlugin;
import org.killbill.billing.util.callcontext.CallContext;
+import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.ImmutableList;
public class PaymentStateContext {
@@ -56,6 +57,8 @@ public class PaymentStateContext {
// Set in the control layer in the leavingState callback
private String paymentExternalKey;
private String paymentTransactionExternalKey;
+ protected UUID paymentIdForNewPayment;
+ protected UUID paymentTransactionIdForNewPaymentTransaction;
// Set in the control layer after creating the attempt in the enteringState callback
private UUID attemptId;
@@ -72,20 +75,20 @@ public class PaymentStateContext {
private final CallContext callContext;
private final boolean isApiPayment;
- // Use to create new transactions only
+ @VisibleForTesting
public PaymentStateContext(final boolean isApiPayment, @Nullable final UUID paymentId, @Nullable final String paymentTransactionExternalKey, final TransactionType transactionType,
final Account account, @Nullable final UUID paymentMethodId, final BigDecimal amount, final Currency currency,
final boolean shouldLockAccountAndDispatch, final Iterable<PluginProperty> properties,
final InternalCallContext internalCallContext, final CallContext callContext) {
this(isApiPayment, paymentId, null, null, null, paymentTransactionExternalKey, transactionType, account, paymentMethodId,
- amount, currency, shouldLockAccountAndDispatch, null, properties, internalCallContext, callContext);
+ amount, currency, null, null, shouldLockAccountAndDispatch, null, properties, internalCallContext, callContext);
}
// Used to create new payment and transactions
public PaymentStateContext(final boolean isApiPayment, @Nullable final UUID paymentId, final UUID transactionId, @Nullable final UUID attemptId, @Nullable final String paymentExternalKey,
@Nullable final String paymentTransactionExternalKey, final TransactionType transactionType,
final Account account, @Nullable final UUID paymentMethodId, final BigDecimal amount, final Currency currency,
- final boolean shouldLockAccountAndDispatch, final OperationResult overridePluginOperationResult, final Iterable<PluginProperty> properties,
+ @Nullable final UUID paymentIdForNewPayment, @Nullable final UUID paymentTransactionIdForNewPaymentTransaction, final boolean shouldLockAccountAndDispatch, final OperationResult overridePluginOperationResult, final Iterable<PluginProperty> properties,
final InternalCallContext internalCallContext, final CallContext callContext) {
this.isApiPayment = isApiPayment;
this.paymentId = paymentId;
@@ -98,6 +101,8 @@ public class PaymentStateContext {
this.paymentMethodId = paymentMethodId;
this.amount = amount;
this.currency = currency;
+ this.paymentIdForNewPayment = paymentIdForNewPayment;
+ this.paymentTransactionIdForNewPaymentTransaction = paymentTransactionIdForNewPaymentTransaction;
this.shouldLockAccountAndDispatch = shouldLockAccountAndDispatch;
this.overridePluginOperationResult = overridePluginOperationResult;
this.properties = properties;
@@ -234,4 +239,11 @@ public class PaymentStateContext {
this.properties = properties;
}
+ public UUID getPaymentIdForNewPayment() {
+ return paymentIdForNewPayment;
+ }
+
+ public UUID getPaymentTransactionIdForNewPaymentTransaction() {
+ return paymentTransactionIdForNewPaymentTransaction;
+ }
}
diff --git a/payment/src/main/java/org/killbill/billing/payment/core/sm/PluginControlPaymentAutomatonRunner.java b/payment/src/main/java/org/killbill/billing/payment/core/sm/PluginControlPaymentAutomatonRunner.java
index d4b2b39..1ec0b0d 100644
--- a/payment/src/main/java/org/killbill/billing/payment/core/sm/PluginControlPaymentAutomatonRunner.java
+++ b/payment/src/main/java/org/killbill/billing/payment/core/sm/PluginControlPaymentAutomatonRunner.java
@@ -1,6 +1,6 @@
/*
- * Copyright 2014-2016 Groupon, Inc
- * Copyright 2014-2016 The Billing Project, LLC
+ * Copyright 2014-2017 Groupon, Inc
+ * Copyright 2014-2017 The Billing Project, LLC
*
* The Billing Project licenses this file to you under the Apache License, version 2.0
* (the "License"); you may not use this file except in compliance with the
@@ -43,6 +43,7 @@ import org.killbill.billing.payment.api.PaymentApiException;
import org.killbill.billing.payment.api.PluginProperty;
import org.killbill.billing.payment.api.TransactionType;
import org.killbill.billing.payment.core.PaymentExecutors;
+import org.killbill.billing.payment.core.PaymentPluginServiceRegistration;
import org.killbill.billing.payment.core.PaymentProcessor;
import org.killbill.billing.payment.core.sm.control.AuthorizeControlOperation;
import org.killbill.billing.payment.core.sm.control.CaptureControlOperation;
@@ -60,7 +61,6 @@ import org.killbill.billing.payment.core.sm.control.PurchaseControlOperation;
import org.killbill.billing.payment.core.sm.control.RefundControlOperation;
import org.killbill.billing.payment.core.sm.control.VoidControlOperation;
import org.killbill.billing.payment.dao.PaymentDao;
-import org.killbill.billing.payment.plugin.api.PaymentPluginApi;
import org.killbill.billing.payment.retry.BaseRetryService.RetryServiceScheduler;
import org.killbill.billing.util.callcontext.CallContext;
import org.killbill.billing.util.config.definition.PaymentConfig;
@@ -70,7 +70,6 @@ import org.killbill.commons.locker.GlobalLocker;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.MoreObjects;
-import com.google.common.base.Objects;
import static org.killbill.billing.payment.glue.PaymentModule.RETRYABLE_NAMED;
@@ -96,11 +95,11 @@ public class PluginControlPaymentAutomatonRunner extends PaymentAutomatonRunner
private final PaymentConfig paymentConfig;
@Inject
- public PluginControlPaymentAutomatonRunner(final PaymentDao paymentDao, final GlobalLocker locker, final OSGIServiceRegistration<PaymentPluginApi> pluginRegistry,
+ public PluginControlPaymentAutomatonRunner(final PaymentDao paymentDao, final GlobalLocker locker, final PaymentPluginServiceRegistration paymentPluginServiceRegistration,
final OSGIServiceRegistration<PaymentControlPluginApi> paymentControlPluginRegistry, final Clock clock, final PaymentProcessor paymentProcessor, @Named(RETRYABLE_NAMED) final RetryServiceScheduler retryServiceScheduler,
final PaymentConfig paymentConfig, final PaymentExecutors executors, final PaymentStateMachineHelper paymentSMHelper, final PaymentControlStateMachineHelper paymentControlStateMachineHelper,
final ControlPluginRunner controlPluginRunner, final PersistentBus eventBus) {
- super(paymentConfig, paymentDao, locker, pluginRegistry, clock, executors, eventBus, paymentSMHelper);
+ super(paymentConfig, paymentDao, locker, paymentPluginServiceRegistration, clock, executors, eventBus, paymentSMHelper);
this.paymentProcessor = paymentProcessor;
this.paymentControlPluginRegistry = paymentControlPluginRegistry;
this.retryServiceScheduler = retryServiceScheduler;
@@ -272,7 +271,7 @@ public class PluginControlPaymentAutomatonRunner extends PaymentAutomatonRunner
paymentControlStateMachineHelper.getInitialState().runOperation(paymentControlStateMachineHelper.getOperation(), callback, enteringStateCallback, leavingStateCallback);
} catch (final MissingEntryException e) {
- throw new PaymentApiException(e.getCause(), ErrorCode.PAYMENT_INTERNAL_ERROR, Objects.firstNonNull(e.getMessage(), ""));
+ throw new PaymentApiException(e.getCause(), ErrorCode.PAYMENT_INTERNAL_ERROR, MoreObjects.firstNonNull(e.getMessage(), ""));
} catch (final OperationException e) {
if (e.getCause() instanceof PaymentApiException) {
throw (PaymentApiException) e.getCause();
diff --git a/payment/src/main/java/org/killbill/billing/payment/dao/PaymentMethodModelDao.java b/payment/src/main/java/org/killbill/billing/payment/dao/PaymentMethodModelDao.java
index 7e284de..84f157b 100644
--- a/payment/src/main/java/org/killbill/billing/payment/dao/PaymentMethodModelDao.java
+++ b/payment/src/main/java/org/killbill/billing/payment/dao/PaymentMethodModelDao.java
@@ -24,11 +24,10 @@ import org.joda.time.DateTime;
import org.killbill.billing.payment.api.PaymentMethod;
import org.killbill.billing.util.dao.TableName;
-import org.killbill.billing.entity.EntityBase;
import org.killbill.billing.util.entity.dao.EntityModelDao;
import org.killbill.billing.util.entity.dao.EntityModelDaoBase;
-import com.google.common.base.Objects;
+import com.google.common.base.MoreObjects;
public class PaymentMethodModelDao extends EntityModelDaoBase implements EntityModelDao<PaymentMethod> {
@@ -43,7 +42,7 @@ public class PaymentMethodModelDao extends EntityModelDaoBase implements EntityM
final UUID accountId, final String pluginName,
final Boolean isActive) {
super(id, createdDate, updatedDate);
- this.externalKey = Objects.firstNonNull(externalKey, id.toString());
+ this.externalKey = MoreObjects.firstNonNull(externalKey, id.toString());
this.accountId = accountId;
this.pluginName = pluginName;
this.isActive = isActive;
diff --git a/payment/src/main/java/org/killbill/billing/payment/dao/PaymentModelDao.java b/payment/src/main/java/org/killbill/billing/payment/dao/PaymentModelDao.java
index b15be00..7db4e0d 100644
--- a/payment/src/main/java/org/killbill/billing/payment/dao/PaymentModelDao.java
+++ b/payment/src/main/java/org/killbill/billing/payment/dao/PaymentModelDao.java
@@ -28,7 +28,7 @@ import org.killbill.billing.util.entity.dao.EntityModelDao;
import org.killbill.billing.util.entity.dao.EntityModelDaoBase;
import org.killbill.billing.util.UUIDs;
-import com.google.common.base.Objects;
+import com.google.common.base.MoreObjects;
public class PaymentModelDao extends EntityModelDaoBase implements EntityModelDao<Payment> {
@@ -49,12 +49,17 @@ public class PaymentModelDao extends EntityModelDaoBase implements EntityModelDa
this.accountId = accountId;
this.paymentMethodId = paymentMethodId;
this.paymentNumber = paymentNumber;
- this.externalKey = Objects.firstNonNull(externalKey, id.toString());
+ this.externalKey = MoreObjects.firstNonNull(externalKey, id.toString());
+ }
+
+ public PaymentModelDao(final UUID id, @Nullable final DateTime createdDate, @Nullable final DateTime updatedDate, final UUID accountId,
+ final UUID paymentMethodId, @Nullable final String externalKey) {
+ this(id, createdDate, updatedDate, accountId, paymentMethodId, INVALID_PAYMENT_NUMBER, externalKey);
}
public PaymentModelDao(@Nullable final DateTime createdDate, @Nullable final DateTime updatedDate, final UUID accountId,
final UUID paymentMethodId, @Nullable final String externalKey) {
- this(UUIDs.randomUUID(), createdDate, updatedDate, accountId, paymentMethodId, INVALID_PAYMENT_NUMBER, externalKey);
+ this(UUIDs.randomUUID(), createdDate, updatedDate, accountId, paymentMethodId, externalKey);
}
public UUID getAccountId() { return accountId; }
diff --git a/payment/src/main/java/org/killbill/billing/payment/dao/PaymentTransactionModelDao.java b/payment/src/main/java/org/killbill/billing/payment/dao/PaymentTransactionModelDao.java
index 3f11cf9..88d40c6 100644
--- a/payment/src/main/java/org/killbill/billing/payment/dao/PaymentTransactionModelDao.java
+++ b/payment/src/main/java/org/killbill/billing/payment/dao/PaymentTransactionModelDao.java
@@ -32,7 +32,7 @@ import org.killbill.billing.util.dao.TableName;
import org.killbill.billing.util.entity.dao.EntityModelDao;
import org.killbill.billing.util.entity.dao.EntityModelDaoBase;
-import com.google.common.base.Objects;
+import com.google.common.base.MoreObjects;
public class PaymentTransactionModelDao extends EntityModelDaoBase implements EntityModelDao<PaymentTransaction> {
@@ -57,7 +57,7 @@ public class PaymentTransactionModelDao extends EntityModelDaoBase implements En
final TransactionStatus paymentStatus, final BigDecimal amount, final Currency currency, final String gatewayErrorCode, final String gatewayErrorMsg) {
super(id, createdDate, updatedDate);
this.attemptId = attemptId;
- this.transactionExternalKey = Objects.firstNonNull(transactionExternalKey, id.toString());
+ this.transactionExternalKey = MoreObjects.firstNonNull(transactionExternalKey, id.toString());
this.paymentId = paymentId;
this.transactionType = transactionType;
this.effectiveDate = effectiveDate;
@@ -70,10 +70,16 @@ public class PaymentTransactionModelDao extends EntityModelDaoBase implements En
this.gatewayErrorMsg = gatewayErrorMsg;
}
+ public PaymentTransactionModelDao(final UUID id, @Nullable final DateTime createdDate, @Nullable final DateTime updatedDate, @Nullable final UUID attemptId,
+ @Nullable final String transactionExternalKey, final UUID paymentId, final TransactionType transactionType, final DateTime effectiveDate,
+ final TransactionStatus paymentStatus, final BigDecimal amount, final Currency currency, final String gatewayErrorCode, final String gatewayErrorMsg) {
+ this(id, attemptId, transactionExternalKey, createdDate, updatedDate, paymentId, transactionType, effectiveDate, paymentStatus, amount, currency, gatewayErrorCode, gatewayErrorMsg);
+ }
+
public PaymentTransactionModelDao(@Nullable final DateTime createdDate, @Nullable final DateTime updatedDate, @Nullable final UUID attemptId,
@Nullable final String transactionExternalKey, final UUID paymentId, final TransactionType transactionType, final DateTime effectiveDate,
final TransactionStatus paymentStatus, final BigDecimal amount, final Currency currency, final String gatewayErrorCode, final String gatewayErrorMsg) {
- this(UUIDs.randomUUID(), attemptId, transactionExternalKey, createdDate, updatedDate, paymentId, transactionType, effectiveDate, paymentStatus, amount, currency, gatewayErrorCode, gatewayErrorMsg);
+ this(UUIDs.randomUUID(), createdDate, updatedDate, attemptId, transactionExternalKey, paymentId, transactionType, effectiveDate, paymentStatus, amount, currency, gatewayErrorCode, gatewayErrorMsg);
}
public UUID getPaymentId() {
diff --git a/payment/src/main/java/org/killbill/billing/payment/glue/PaymentModule.java b/payment/src/main/java/org/killbill/billing/payment/glue/PaymentModule.java
index 0a31eee..7586a8b 100644
--- a/payment/src/main/java/org/killbill/billing/payment/glue/PaymentModule.java
+++ b/payment/src/main/java/org/killbill/billing/payment/glue/PaymentModule.java
@@ -1,7 +1,7 @@
/*
* Copyright 2010-2014 Ning, Inc.
- * Copyright 2014-2016 Groupon, Inc
- * Copyright 2014-2016 The Billing Project, LLC
+ * Copyright 2014-2017 Groupon, Inc
+ * Copyright 2014-2017 The Billing Project, LLC
*
* The Billing Project licenses this file to you under the Apache License, version 2.0
* (the "License"); you may not use this file except in compliance with the
@@ -39,6 +39,7 @@ import org.killbill.billing.payment.caching.StateMachineConfigCacheInvalidationC
import org.killbill.billing.payment.core.PaymentExecutors;
import org.killbill.billing.payment.core.PaymentGatewayProcessor;
import org.killbill.billing.payment.core.PaymentMethodProcessor;
+import org.killbill.billing.payment.core.PaymentPluginServiceRegistration;
import org.killbill.billing.payment.core.PaymentProcessor;
import org.killbill.billing.payment.core.PluginControlPaymentProcessor;
import org.killbill.billing.payment.core.janitor.IncompletePaymentAttemptTask;
@@ -147,6 +148,8 @@ public class PaymentModule extends KillBillModule {
bind(new TypeLiteral<OSGIServiceRegistration<PaymentPluginApi>>() {}).toProvider(DefaultPaymentProviderPluginRegistryProvider.class).asEagerSingleton();
bind(new TypeLiteral<OSGIServiceRegistration<PaymentControlPluginApi>>() {}).toProvider(DefaultPaymentControlProviderPluginRegistryProvider.class).asEagerSingleton();
+ bind(PaymentPluginServiceRegistration.class).asEagerSingleton();
+
bind(PaymentApi.class).to(DefaultPaymentApi.class).asEagerSingleton();
bind(PaymentGatewayApi.class).to(DefaultPaymentGatewayApi.class).asEagerSingleton();
bind(AdminPaymentApi.class).to(DefaultAdminPaymentApi.class).asEagerSingleton();
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 d027222..f03d42f 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
@@ -77,7 +77,6 @@ import org.slf4j.LoggerFactory;
import com.google.common.base.Function;
import com.google.common.base.MoreObjects;
-import com.google.common.base.Objects;
import com.google.common.base.Preconditions;
import com.google.common.base.Predicate;
import com.google.common.collect.Collections2;
@@ -468,7 +467,7 @@ public final class InvoicePaymentControlPluginApi implements PaymentControlPlugi
(specifiedItemAmount.compareTo(BigDecimal.ZERO) <= 0 || specifiedItemAmount.compareTo(itemAmount) > 0)) {
throw new PaymentControlApiException("Failed to compute refund: ", new PaymentApiException(ErrorCode.PAYMENT_PLUGIN_EXCEPTION, "You need to specify a valid invoice item amount"));
}
- amountFromItems = amountFromItems.add(Objects.firstNonNull(specifiedItemAmount, itemAmount));
+ amountFromItems = amountFromItems.add(MoreObjects.firstNonNull(specifiedItemAmount, itemAmount));
}
return amountFromItems;
} catch (final InvoiceApiException e) {
diff --git a/payment/src/main/resources/org/killbill/billing/payment/ddl.sql b/payment/src/main/resources/org/killbill/billing/payment/ddl.sql
index e994fa9..24d99c5 100644
--- a/payment/src/main/resources/org/killbill/billing/payment/ddl.sql
+++ b/payment/src/main/resources/org/killbill/billing/payment/ddl.sql
@@ -163,7 +163,7 @@ CREATE TABLE payment_transactions (
processed_currency varchar(3),
payment_id varchar(36) NOT NULL,
gateway_error_code varchar(32),
- gateway_error_msg varchar(256),
+ gateway_error_msg text,
created_by varchar(50) NOT NULL,
created_date datetime NOT NULL,
updated_by varchar(50) NOT NULL,
@@ -194,7 +194,7 @@ CREATE TABLE payment_transaction_history (
processed_currency varchar(3),
payment_id varchar(36) NOT NULL,
gateway_error_code varchar(32),
- gateway_error_msg varchar(256),
+ gateway_error_msg text,
change_type varchar(6) NOT NULL,
created_by varchar(50) NOT NULL,
created_date datetime NOT NULL,
diff --git a/payment/src/main/resources/org/killbill/billing/payment/migration/V20170302005722__enlarge_gateway_error_msg.sql b/payment/src/main/resources/org/killbill/billing/payment/migration/V20170302005722__enlarge_gateway_error_msg.sql
new file mode 100644
index 0000000..cf237df
--- /dev/null
+++ b/payment/src/main/resources/org/killbill/billing/payment/migration/V20170302005722__enlarge_gateway_error_msg.sql
@@ -0,0 +1,2 @@
+alter table payment_transactions modify gateway_error_msg text;
+alter table payment_transaction_history modify gateway_error_msg text;
diff --git a/payment/src/test/java/org/killbill/billing/payment/core/sm/MockRetryablePaymentAutomatonRunner.java b/payment/src/test/java/org/killbill/billing/payment/core/sm/MockRetryablePaymentAutomatonRunner.java
index 24ae2a7..350bf9f 100644
--- a/payment/src/test/java/org/killbill/billing/payment/core/sm/MockRetryablePaymentAutomatonRunner.java
+++ b/payment/src/test/java/org/killbill/billing/payment/core/sm/MockRetryablePaymentAutomatonRunner.java
@@ -1,6 +1,6 @@
/*
- * Copyright 2014-2016 Groupon, Inc
- * Copyright 2014-2016 The Billing Project, LLC
+ * Copyright 2014-2017 Groupon, Inc
+ * Copyright 2014-2017 The Billing Project, LLC
*
* The Billing Project licenses this file to you under the Apache License, version 2.0
* (the "License"); you may not use this file except in compliance with the
@@ -36,12 +36,12 @@ import org.killbill.billing.payment.api.PaymentApiException;
import org.killbill.billing.payment.api.PluginProperty;
import org.killbill.billing.payment.api.TransactionType;
import org.killbill.billing.payment.core.PaymentExecutors;
+import org.killbill.billing.payment.core.PaymentPluginServiceRegistration;
import org.killbill.billing.payment.core.PaymentProcessor;
import org.killbill.billing.payment.core.sm.control.ControlPluginRunner;
import org.killbill.billing.payment.core.sm.control.PaymentStateControlContext;
import org.killbill.billing.payment.dao.PaymentDao;
import org.killbill.billing.payment.dispatcher.PluginDispatcher;
-import org.killbill.billing.payment.plugin.api.PaymentPluginApi;
import org.killbill.billing.payment.retry.BaseRetryService.RetryServiceScheduler;
import org.killbill.billing.tag.TagInternalApi;
import org.killbill.billing.util.callcontext.CallContext;
@@ -58,10 +58,10 @@ public class MockRetryablePaymentAutomatonRunner extends PluginControlPaymentAut
private PaymentStateControlContext context;
@Inject
- public MockRetryablePaymentAutomatonRunner(final PaymentDao paymentDao, final GlobalLocker locker, final OSGIServiceRegistration<PaymentPluginApi> pluginRegistry, final OSGIServiceRegistration<PaymentControlPluginApi> retryPluginRegistry, final Clock clock, final TagInternalApi tagApi, final PaymentProcessor paymentProcessor,
+ public MockRetryablePaymentAutomatonRunner(final PaymentDao paymentDao, final GlobalLocker locker, final PaymentPluginServiceRegistration paymentPluginServiceRegistration, final OSGIServiceRegistration<PaymentControlPluginApi> retryPluginRegistry, final Clock clock, final TagInternalApi tagApi, final PaymentProcessor paymentProcessor,
@Named(RETRYABLE_NAMED) final RetryServiceScheduler retryServiceScheduler, final PaymentConfig paymentConfig, final PaymentExecutors executors,
final PaymentStateMachineHelper paymentSMHelper, final PaymentControlStateMachineHelper retrySMHelper, final ControlPluginRunner controlPluginRunner, final PersistentBus eventBus) {
- super(paymentDao, locker, pluginRegistry, retryPluginRegistry, clock, paymentProcessor, retryServiceScheduler, paymentConfig, executors, paymentSMHelper, retrySMHelper, controlPluginRunner, eventBus);
+ super(paymentDao, locker, paymentPluginServiceRegistration, retryPluginRegistry, clock, paymentProcessor, retryServiceScheduler, paymentConfig, executors, paymentSMHelper, retrySMHelper, controlPluginRunner, eventBus);
}
@Override
diff --git a/payment/src/test/java/org/killbill/billing/payment/core/sm/TestPaymentAutomatonDAOHelper.java b/payment/src/test/java/org/killbill/billing/payment/core/sm/TestPaymentAutomatonDAOHelper.java
index 65586e8..88655c2 100644
--- a/payment/src/test/java/org/killbill/billing/payment/core/sm/TestPaymentAutomatonDAOHelper.java
+++ b/payment/src/test/java/org/killbill/billing/payment/core/sm/TestPaymentAutomatonDAOHelper.java
@@ -1,6 +1,6 @@
/*
- * Copyright 2014 Groupon, Inc
- * Copyright 2014 The Billing Project, LLC
+ * Copyright 2014-2017 Groupon, Inc
+ * Copyright 2014-2017 The Billing Project, LLC
*
* Groupon licenses this file to you under the Apache License, version 2.0
* (the "License"); you may not use this file except in compliance with the
@@ -114,8 +114,10 @@ public class TestPaymentAutomatonDAOHelper extends PaymentTestSuiteWithEmbeddedD
Mockito.when(account.getId()).thenReturn(UUID.randomUUID());
// No default payment method
- paymentStateContext = new PaymentStateContext(true, paymentId,
- null, null,
+ paymentStateContext = new PaymentStateContext(true,
+ paymentId,
+ null,
+ null,
paymentExternalKey,
paymentTransactionExternalKey,
TransactionType.CAPTURE,
@@ -123,11 +125,14 @@ public class TestPaymentAutomatonDAOHelper extends PaymentTestSuiteWithEmbeddedD
UUID.randomUUID(),
amount,
currency,
+ null,
+ null,
false,
- null, ImmutableList.<PluginProperty>of(),
+ null,
+ ImmutableList.<PluginProperty>of(),
internalCallContext,
callContext);
- return new PaymentAutomatonDAOHelper(paymentStateContext, clock.getUTCNow(), paymentDao, registry, internalCallContext, eventBus, paymentSMHelper);
+ return new PaymentAutomatonDAOHelper(paymentStateContext, clock.getUTCNow(), paymentDao, paymentPluginServiceRegistration, internalCallContext, eventBus, paymentSMHelper);
}
}
diff --git a/payment/src/test/java/org/killbill/billing/payment/core/sm/TestPaymentEnteringStateCallback.java b/payment/src/test/java/org/killbill/billing/payment/core/sm/TestPaymentEnteringStateCallback.java
index e146a6e..287e297 100644
--- a/payment/src/test/java/org/killbill/billing/payment/core/sm/TestPaymentEnteringStateCallback.java
+++ b/payment/src/test/java/org/killbill/billing/payment/core/sm/TestPaymentEnteringStateCallback.java
@@ -1,6 +1,6 @@
/*
- * Copyright 2014 Groupon, Inc
- * Copyright 2014 The Billing Project, LLC
+ * Copyright 2014-2017 Groupon, Inc
+ * Copyright 2014-2017 The Billing Project, LLC
*
* Groupon licenses this file to you under the Apache License, version 2.0
* (the "License"); you may not use this file except in compliance with the
@@ -69,7 +69,7 @@ public class TestPaymentEnteringStateCallback extends PaymentTestSuiteWithEmbedd
ImmutableList.<PluginProperty>of(),
internalCallContext,
callContext);
- daoHelper = new PaymentAutomatonDAOHelper(paymentStateContext, clock.getUTCNow(), paymentDao, registry, internalCallContext, eventBus, paymentSMHelper);
+ daoHelper = new PaymentAutomatonDAOHelper(paymentStateContext, clock.getUTCNow(), paymentDao, paymentPluginServiceRegistration, internalCallContext, eventBus, paymentSMHelper);
callback = new PaymentEnteringStateTestCallback(daoHelper, paymentStateContext);
Mockito.when(state.getName()).thenReturn("NEW_STATE");
diff --git a/payment/src/test/java/org/killbill/billing/payment/core/sm/TestPaymentLeavingStateCallback.java b/payment/src/test/java/org/killbill/billing/payment/core/sm/TestPaymentLeavingStateCallback.java
index 38a1e9b..6b1f308 100644
--- a/payment/src/test/java/org/killbill/billing/payment/core/sm/TestPaymentLeavingStateCallback.java
+++ b/payment/src/test/java/org/killbill/billing/payment/core/sm/TestPaymentLeavingStateCallback.java
@@ -1,6 +1,6 @@
/*
- * Copyright 2014-2016 Groupon, Inc
- * Copyright 2014-2016 The Billing Project, LLC
+ * Copyright 2014-2017 Groupon, Inc
+ * Copyright 2014-2017 The Billing Project, LLC
*
* The Billing Project licenses this file to you under the Apache License, version 2.0
* (the "License"); you may not use this file except in compliance with the
@@ -23,10 +23,8 @@ import java.util.UUID;
import javax.annotation.Nullable;
-import org.killbill.automaton.OperationException;
import org.killbill.automaton.State;
import org.killbill.billing.account.api.Account;
-import org.killbill.billing.callcontext.InternalCallContext;
import org.killbill.billing.catalog.api.Currency;
import org.killbill.billing.payment.PaymentTestSuiteWithEmbeddedDB;
import org.killbill.billing.payment.api.PaymentApiException;
@@ -114,6 +112,8 @@ public class TestPaymentLeavingStateCallback extends PaymentTestSuiteWithEmbedde
UUID.randomUUID(),
new BigDecimal("192.3920111"),
Currency.BRL,
+ null,
+ null,
false,
null, ImmutableList.<PluginProperty>of(),
internalCallContext,
@@ -144,7 +144,7 @@ public class TestPaymentLeavingStateCallback extends PaymentTestSuiteWithEmbedde
paymentDao.insertPaymentWithFirstTransaction(newPaymentModelDao, newPaymentTransactionModelDao, internalCallContext);
}
- final PaymentAutomatonDAOHelper daoHelper = new PaymentAutomatonDAOHelper(paymentStateContext, clock.getUTCNow(), paymentDao, registry, internalCallContext, eventBus, paymentSMHelper);
+ final PaymentAutomatonDAOHelper daoHelper = new PaymentAutomatonDAOHelper(paymentStateContext, clock.getUTCNow(), paymentDao, paymentPluginServiceRegistration, internalCallContext, eventBus, paymentSMHelper);
callback = new PaymentLeavingStateTestCallback(daoHelper, paymentStateContext);
Mockito.when(state.getName()).thenReturn("NEW_STATE");
diff --git a/payment/src/test/java/org/killbill/billing/payment/core/sm/TestPaymentOperation.java b/payment/src/test/java/org/killbill/billing/payment/core/sm/TestPaymentOperation.java
index cc32c53..8ee5efb 100644
--- a/payment/src/test/java/org/killbill/billing/payment/core/sm/TestPaymentOperation.java
+++ b/payment/src/test/java/org/killbill/billing/payment/core/sm/TestPaymentOperation.java
@@ -1,6 +1,6 @@
/*
- * Copyright 2014 Groupon, Inc
- * Copyright 2014 The Billing Project, LLC
+ * Copyright 2014-2017 Groupon, Inc
+ * Copyright 2014-2017 The Billing Project, LLC
*
* Groupon licenses this file to you under the Apache License, version 2.0
* (the "License"); you may not use this file except in compliance with the
@@ -30,6 +30,7 @@ import org.killbill.billing.payment.PaymentTestSuiteNoDB;
import org.killbill.billing.payment.api.PaymentApiException;
import org.killbill.billing.payment.api.PluginProperty;
import org.killbill.billing.payment.api.TransactionType;
+import org.killbill.billing.payment.core.PaymentPluginServiceRegistration;
import org.killbill.billing.payment.core.sm.payments.PaymentOperation;
import org.killbill.billing.payment.dao.PaymentDao;
import org.killbill.billing.payment.dao.PaymentMethodModelDao;
@@ -115,6 +116,8 @@ public class TestPaymentOperation extends PaymentTestSuiteNoDB {
UUID.randomUUID(),
new BigDecimal("192.3920111"),
Currency.BRL,
+ null,
+ null,
false,
null, ImmutableList.<PluginProperty>of(),
internalCallContext,
@@ -125,7 +128,8 @@ public class TestPaymentOperation extends PaymentTestSuiteNoDB {
final PaymentDao paymentDao = Mockito.mock(PaymentDao.class);
Mockito.when(paymentDao.getPaymentMethod(paymentStateContext.getPaymentMethodId(), internalCallContext)).thenReturn(paymentMethodModelDao);
- final PaymentAutomatonDAOHelper daoHelper = new PaymentAutomatonDAOHelper(paymentStateContext, clock.getUTCNow(), paymentDao, registry, internalCallContext, eventBus, paymentSMHelper);
+ final PaymentPluginServiceRegistration paymentPluginServiceRegistration = new PaymentPluginServiceRegistration(paymentDao, registry);
+ final PaymentAutomatonDAOHelper daoHelper = new PaymentAutomatonDAOHelper(paymentStateContext, clock.getUTCNow(), paymentDao, paymentPluginServiceRegistration, internalCallContext, eventBus, paymentSMHelper);
paymentOperation = new PaymentOperationTest(paymentPluginStatus, daoHelper, locker, paymentPluginDispatcher, paymentConfig, paymentStateContext);
}
diff --git a/payment/src/test/java/org/killbill/billing/payment/core/sm/TestPluginOperation.java b/payment/src/test/java/org/killbill/billing/payment/core/sm/TestPluginOperation.java
index ea77ad2..294a5f8 100644
--- a/payment/src/test/java/org/killbill/billing/payment/core/sm/TestPluginOperation.java
+++ b/payment/src/test/java/org/killbill/billing/payment/core/sm/TestPluginOperation.java
@@ -206,8 +206,10 @@ public class TestPluginOperation extends PaymentTestSuiteNoDB {
private PaymentOperation getPluginOperation(final boolean shouldLockAccount, final int timeoutSeconds) throws PaymentApiException {
final PluginDispatcher<OperationResult> paymentPluginDispatcher = new PluginDispatcher<OperationResult>(timeoutSeconds, paymentExecutors);
- final PaymentStateContext paymentStateContext = new PaymentStateContext(true, UUID.randomUUID(),
- null, null,
+ final PaymentStateContext paymentStateContext = new PaymentStateContext(true,
+ UUID.randomUUID(),
+ null,
+ null,
UUID.randomUUID().toString(),
UUID.randomUUID().toString(),
TransactionType.CAPTURE,
@@ -215,8 +217,11 @@ public class TestPluginOperation extends PaymentTestSuiteNoDB {
UUID.randomUUID(),
new BigDecimal("192.3920111"),
Currency.BRL,
+ null,
+ null,
shouldLockAccount,
- null, ImmutableList.<PluginProperty>of(),
+ null,
+ ImmutableList.<PluginProperty>of(),
internalCallContext,
callContext);
diff --git a/payment/src/test/java/org/killbill/billing/payment/core/sm/TestRetryablePayment.java b/payment/src/test/java/org/killbill/billing/payment/core/sm/TestRetryablePayment.java
index 752f40c..0be4a2f 100644
--- a/payment/src/test/java/org/killbill/billing/payment/core/sm/TestRetryablePayment.java
+++ b/payment/src/test/java/org/killbill/billing/payment/core/sm/TestRetryablePayment.java
@@ -1,6 +1,6 @@
/*
- * Copyright 2014-2016 Groupon, Inc
- * Copyright 2014-2016 The Billing Project, LLC
+ * Copyright 2014-2017 Groupon, Inc
+ * Copyright 2014-2017 The Billing Project, LLC
*
* The Billing Project licenses this file to you under the Apache License, version 2.0
* (the "License"); you may not use this file except in compliance with the
@@ -160,7 +160,7 @@ public class TestRetryablePayment extends PaymentTestSuiteNoDB {
runner = new MockRetryablePaymentAutomatonRunner(
paymentDao,
locker,
- pluginRegistry,
+ paymentPluginServiceRegistration,
retryPluginRegistry,
clock,
tagApi,
@@ -200,7 +200,7 @@ public class TestRetryablePayment extends PaymentTestSuiteNoDB {
paymentDao,
clock);
- processor = new PluginControlPaymentProcessor(pluginRegistry,
+ processor = new PluginControlPaymentProcessor(paymentPluginServiceRegistration,
accountInternalApi,
null,
tagApi,
diff --git a/payment/src/test/java/org/killbill/billing/payment/core/TestPaymentProcessor.java b/payment/src/test/java/org/killbill/billing/payment/core/TestPaymentProcessor.java
index 754d1fe..3e026fa 100644
--- a/payment/src/test/java/org/killbill/billing/payment/core/TestPaymentProcessor.java
+++ b/payment/src/test/java/org/killbill/billing/payment/core/TestPaymentProcessor.java
@@ -81,7 +81,7 @@ public class TestPaymentProcessor extends PaymentTestSuiteWithEmbeddedDB {
final String authorizationKey = UUID.randomUUID().toString();
final Payment authorization = paymentProcessor.createAuthorization(true, null, account, null, null, TEN, CURRENCY, paymentExternalKey, authorizationKey,
- SHOULD_LOCK_ACCOUNT, pluginPropertiesToDriveTransationToUnknown, callContext, internalCallContext);
+ null, null, SHOULD_LOCK_ACCOUNT, pluginPropertiesToDriveTransationToUnknown, callContext, internalCallContext);
verifyPayment(authorization, paymentExternalKey, ZERO, ZERO, ZERO, 1);
final UUID paymentId = authorization.getId();
verifyPaymentTransaction(authorization.getTransactions().get(0), authorizationKey, TransactionType.AUTHORIZE, TEN, paymentId);
@@ -105,7 +105,7 @@ public class TestPaymentProcessor extends PaymentTestSuiteWithEmbeddedDB {
// AUTH pre-3DS
final String authorizationKey = UUID.randomUUID().toString();
final Payment authorization = paymentProcessor.createAuthorization(true, null, account, null, null, TEN, CURRENCY, paymentExternalKey, authorizationKey,
- SHOULD_LOCK_ACCOUNT, pluginPropertiesToDriveTransationToPending, callContext, internalCallContext);
+ null, null, SHOULD_LOCK_ACCOUNT, pluginPropertiesToDriveTransationToPending, callContext, internalCallContext);
verifyPayment(authorization, paymentExternalKey, ZERO, ZERO, ZERO, 1);
final UUID paymentId = authorization.getId();
verifyPaymentTransaction(authorization.getTransactions().get(0), authorizationKey, TransactionType.AUTHORIZE, TEN, paymentId);
@@ -113,7 +113,7 @@ public class TestPaymentProcessor extends PaymentTestSuiteWithEmbeddedDB {
// AUTH post-3DS
final Payment authorizationPost3DS = paymentProcessor.createAuthorization(true, null, account, null, paymentId, TEN, CURRENCY, paymentExternalKey, authorizationKey,
- SHOULD_LOCK_ACCOUNT, PLUGIN_PROPERTIES, callContext, internalCallContext);
+ null, null, SHOULD_LOCK_ACCOUNT, PLUGIN_PROPERTIES, callContext, internalCallContext);
verifyPayment(authorizationPost3DS, paymentExternalKey, TEN, ZERO, ZERO, 1);
verifyPaymentTransaction(authorizationPost3DS.getTransactions().get(0), authorizationKey, TransactionType.AUTHORIZE, TEN, paymentId);
paymentBusListener.verify(2, account.getId(), paymentId, TEN, TransactionStatus.SUCCESS);
@@ -121,7 +121,7 @@ public class TestPaymentProcessor extends PaymentTestSuiteWithEmbeddedDB {
// CAPTURE
final String capture1Key = UUID.randomUUID().toString();
final Payment partialCapture1 = paymentProcessor.createCapture(true, null, account, paymentId, FIVE, CURRENCY, capture1Key,
- SHOULD_LOCK_ACCOUNT, PLUGIN_PROPERTIES, callContext, internalCallContext);
+ null, SHOULD_LOCK_ACCOUNT, PLUGIN_PROPERTIES, callContext, internalCallContext);
verifyPayment(partialCapture1, paymentExternalKey, TEN, FIVE, ZERO, 2);
verifyPaymentTransaction(partialCapture1.getTransactions().get(1), capture1Key, TransactionType.CAPTURE, FIVE, paymentId);
paymentBusListener.verify(3, account.getId(), paymentId, FIVE, TransactionStatus.SUCCESS);
@@ -129,7 +129,7 @@ public class TestPaymentProcessor extends PaymentTestSuiteWithEmbeddedDB {
// CAPTURE
final String capture2Key = UUID.randomUUID().toString();
final Payment partialCapture2 = paymentProcessor.createCapture(true, null, account, paymentId, FIVE, CURRENCY, capture2Key,
- SHOULD_LOCK_ACCOUNT, PLUGIN_PROPERTIES, callContext, internalCallContext);
+ null, SHOULD_LOCK_ACCOUNT, PLUGIN_PROPERTIES, callContext, internalCallContext);
verifyPayment(partialCapture2, paymentExternalKey, TEN, TEN, ZERO, 3);
verifyPaymentTransaction(partialCapture2.getTransactions().get(2), capture2Key, TransactionType.CAPTURE, FIVE, paymentId);
paymentBusListener.verify(4, account.getId(), paymentId, FIVE, TransactionStatus.SUCCESS);
@@ -137,7 +137,7 @@ public class TestPaymentProcessor extends PaymentTestSuiteWithEmbeddedDB {
// REFUND
final String refund1Key = UUID.randomUUID().toString();
final Payment partialRefund1 = paymentProcessor.createRefund(true, null, account, paymentId, FIVE, CURRENCY, refund1Key,
- SHOULD_LOCK_ACCOUNT, PLUGIN_PROPERTIES, callContext, internalCallContext);
+ null, SHOULD_LOCK_ACCOUNT, PLUGIN_PROPERTIES, callContext, internalCallContext);
verifyPayment(partialRefund1, paymentExternalKey, TEN, TEN, FIVE, 4);
verifyPaymentTransaction(partialRefund1.getTransactions().get(3), refund1Key, TransactionType.REFUND, FIVE, paymentId);
paymentBusListener.verify(5, account.getId(), paymentId, FIVE, TransactionStatus.SUCCESS);
@@ -145,7 +145,7 @@ public class TestPaymentProcessor extends PaymentTestSuiteWithEmbeddedDB {
// REFUND
final String refund2Key = UUID.randomUUID().toString();
final Payment partialRefund2 = paymentProcessor.createRefund(true, null, account, paymentId, FIVE, CURRENCY, refund2Key,
- SHOULD_LOCK_ACCOUNT, PLUGIN_PROPERTIES, callContext, internalCallContext);
+ null, SHOULD_LOCK_ACCOUNT, PLUGIN_PROPERTIES, callContext, internalCallContext);
verifyPayment(partialRefund2, paymentExternalKey, TEN, TEN, TEN, 5);
verifyPaymentTransaction(partialRefund2.getTransactions().get(4), refund2Key, TransactionType.REFUND, FIVE, paymentId);
paymentBusListener.verify(6, account.getId(), paymentId, FIVE, TransactionStatus.SUCCESS);
@@ -158,7 +158,7 @@ public class TestPaymentProcessor extends PaymentTestSuiteWithEmbeddedDB {
// AUTH
final String authorizationKey = UUID.randomUUID().toString();
final Payment authorization = paymentProcessor.createAuthorization(true, null, account, null, null, TEN, CURRENCY, paymentExternalKey, authorizationKey,
- SHOULD_LOCK_ACCOUNT, PLUGIN_PROPERTIES, callContext, internalCallContext);
+ null, null, SHOULD_LOCK_ACCOUNT, PLUGIN_PROPERTIES, callContext, internalCallContext);
verifyPayment(authorization, paymentExternalKey, TEN, ZERO, ZERO, 1);
final UUID paymentId = authorization.getId();
verifyPaymentTransaction(authorization.getTransactions().get(0), authorizationKey, TransactionType.AUTHORIZE, TEN, paymentId);
@@ -167,7 +167,7 @@ public class TestPaymentProcessor extends PaymentTestSuiteWithEmbeddedDB {
// VOID
final String voidKey = UUID.randomUUID().toString();
final Payment voidTransaction = paymentProcessor.createVoid(true, null, account, paymentId, voidKey,
- SHOULD_LOCK_ACCOUNT, PLUGIN_PROPERTIES, callContext, internalCallContext);
+ null, SHOULD_LOCK_ACCOUNT, PLUGIN_PROPERTIES, callContext, internalCallContext);
verifyPayment(voidTransaction, paymentExternalKey, ZERO, ZERO, ZERO, 2);
verifyPaymentTransaction(voidTransaction.getTransactions().get(1), voidKey, TransactionType.VOID, null, paymentId);
paymentBusListener.verify(2, account.getId(), paymentId, null, TransactionStatus.SUCCESS);
@@ -180,7 +180,7 @@ public class TestPaymentProcessor extends PaymentTestSuiteWithEmbeddedDB {
// PURCHASE
final String purchaseKey = UUID.randomUUID().toString();
final Payment purchase = paymentProcessor.createPurchase(true, null, account, null, null, TEN, CURRENCY, paymentExternalKey, purchaseKey,
- SHOULD_LOCK_ACCOUNT, PLUGIN_PROPERTIES, callContext, internalCallContext);
+ null, null, SHOULD_LOCK_ACCOUNT, PLUGIN_PROPERTIES, callContext, internalCallContext);
verifyPayment(purchase, paymentExternalKey, ZERO, ZERO, ZERO, 1);
final UUID paymentId = purchase.getId();
verifyPaymentTransaction(purchase.getTransactions().get(0), purchaseKey, TransactionType.PURCHASE, TEN, paymentId);
@@ -194,7 +194,7 @@ public class TestPaymentProcessor extends PaymentTestSuiteWithEmbeddedDB {
// CREDIT
final String creditKey = UUID.randomUUID().toString();
final Payment purchase = paymentProcessor.createCredit(true, null, account, null, null, TEN, CURRENCY, paymentExternalKey, creditKey,
- SHOULD_LOCK_ACCOUNT, PLUGIN_PROPERTIES, callContext, internalCallContext);
+ null, null, SHOULD_LOCK_ACCOUNT, PLUGIN_PROPERTIES, callContext, internalCallContext);
verifyPayment(purchase, paymentExternalKey, ZERO, ZERO, ZERO, 1);
final UUID paymentId = purchase.getId();
verifyPaymentTransaction(purchase.getTransactions().get(0), creditKey, TransactionType.CREDIT, TEN, paymentId);
@@ -209,7 +209,7 @@ public class TestPaymentProcessor extends PaymentTestSuiteWithEmbeddedDB {
// AUTH
final String authorizationKey = UUID.randomUUID().toString();
final Payment authorization = paymentProcessor.createAuthorization(true, null, account, null, null, TEN, CURRENCY, paymentExternalKey, authorizationKey,
- SHOULD_LOCK_ACCOUNT, pluginPropertiesToDriveTransationToPending, callContext, internalCallContext);
+ null, null, SHOULD_LOCK_ACCOUNT, pluginPropertiesToDriveTransationToPending, callContext, internalCallContext);
verifyPayment(authorization, paymentExternalKey, ZERO, ZERO, ZERO, 1);
final UUID paymentId = authorization.getId();
verifyPaymentTransaction(authorization.getTransactions().get(0), authorizationKey, TransactionType.AUTHORIZE, TEN, paymentId);
@@ -219,7 +219,7 @@ public class TestPaymentProcessor extends PaymentTestSuiteWithEmbeddedDB {
final String refundKey = UUID.randomUUID().toString();
try {
paymentProcessor.createRefund(true, null, account, paymentId, TEN, CURRENCY, refundKey,
- SHOULD_LOCK_ACCOUNT, PLUGIN_PROPERTIES, callContext, internalCallContext);
+ null, SHOULD_LOCK_ACCOUNT, PLUGIN_PROPERTIES, callContext, internalCallContext);
Assert.fail();
} catch (final PaymentApiException e) {
Assert.assertEquals(e.getCode(), ErrorCode.PAYMENT_INVALID_OPERATION.getCode());
@@ -238,7 +238,7 @@ public class TestPaymentProcessor extends PaymentTestSuiteWithEmbeddedDB {
// Create Pending AUTH
final String authorizationKey = UUID.randomUUID().toString();
final Payment authorization = paymentProcessor.createAuthorization(true, null, account, null, null, TEN, CURRENCY, paymentExternalKey, authorizationKey,
- SHOULD_LOCK_ACCOUNT, pluginPropertiesToDriveTransationToPending, callContext, internalCallContext);
+ null, null, SHOULD_LOCK_ACCOUNT, pluginPropertiesToDriveTransationToPending, callContext, internalCallContext);
final PaymentTransaction pendingTransaction = authorization.getTransactions().get(0);
Assert.assertEquals(pendingTransaction.getTransactionStatus(), TransactionStatus.PENDING);
diff --git a/payment/src/test/java/org/killbill/billing/payment/PaymentTestSuiteNoDB.java b/payment/src/test/java/org/killbill/billing/payment/PaymentTestSuiteNoDB.java
index b50ce2c..a41e524 100644
--- a/payment/src/test/java/org/killbill/billing/payment/PaymentTestSuiteNoDB.java
+++ b/payment/src/test/java/org/killbill/billing/payment/PaymentTestSuiteNoDB.java
@@ -1,7 +1,7 @@
/*
* Copyright 2010-2013 Ning, Inc.
- * Copyright 2014-2015 Groupon, Inc
- * Copyright 2014-2015 The Billing Project, LLC
+ * Copyright 2014-2017 Groupon, Inc
+ * Copyright 2014-2017 The Billing Project, LLC
*
* The Billing Project licenses this file to you under the Apache License, version 2.0
* (the "License"); you may not use this file except in compliance with the
@@ -29,6 +29,7 @@ import org.killbill.billing.payment.api.PaymentGatewayApi;
import org.killbill.billing.payment.caching.StateMachineConfigCache;
import org.killbill.billing.payment.core.PaymentExecutors;
import org.killbill.billing.payment.core.PaymentMethodProcessor;
+import org.killbill.billing.payment.core.PaymentPluginServiceRegistration;
import org.killbill.billing.payment.core.PaymentProcessor;
import org.killbill.billing.payment.core.PluginControlPaymentProcessor;
import org.killbill.billing.payment.core.sm.PaymentStateMachineHelper;
@@ -67,6 +68,8 @@ public abstract class PaymentTestSuiteNoDB extends GuicyKillbillTestSuiteNoDB {
@Inject
protected InvoiceInternalApi invoiceApi;
@Inject
+ protected PaymentPluginServiceRegistration paymentPluginServiceRegistration;
+ @Inject
protected OSGIServiceRegistration<PaymentPluginApi> registry;
@Inject
protected PersistentBus eventBus;
diff --git a/payment/src/test/java/org/killbill/billing/payment/PaymentTestSuiteWithEmbeddedDB.java b/payment/src/test/java/org/killbill/billing/payment/PaymentTestSuiteWithEmbeddedDB.java
index 061bbc5..0210596 100644
--- a/payment/src/test/java/org/killbill/billing/payment/PaymentTestSuiteWithEmbeddedDB.java
+++ b/payment/src/test/java/org/killbill/billing/payment/PaymentTestSuiteWithEmbeddedDB.java
@@ -1,7 +1,7 @@
/*
* Copyright 2010-2013 Ning, Inc.
- * Copyright 2014-2016 Groupon, Inc
- * Copyright 2014-2016 The Billing Project, LLC
+ * Copyright 2014-2017 Groupon, Inc
+ * Copyright 2014-2017 The Billing Project, LLC
*
* The Billing Project licenses this file to you under the Apache License, version 2.0
* (the "License"); you may not use this file except in compliance with the
@@ -29,6 +29,7 @@ import org.killbill.billing.payment.api.PaymentGatewayApi;
import org.killbill.billing.payment.caching.StateMachineConfigCache;
import org.killbill.billing.payment.core.PaymentExecutors;
import org.killbill.billing.payment.core.PaymentMethodProcessor;
+import org.killbill.billing.payment.core.PaymentPluginServiceRegistration;
import org.killbill.billing.payment.core.PaymentProcessor;
import org.killbill.billing.payment.core.janitor.IncompletePaymentTransactionTask;
import org.killbill.billing.payment.core.janitor.Janitor;
@@ -69,6 +70,8 @@ public abstract class PaymentTestSuiteWithEmbeddedDB extends GuicyKillbillTestSu
@Inject
protected InvoiceInternalApi invoiceApi;
@Inject
+ protected PaymentPluginServiceRegistration paymentPluginServiceRegistration;
+ @Inject
protected OSGIServiceRegistration<PaymentPluginApi> registry;
@Inject
protected OSGIServiceRegistration<PaymentControlPluginApi> controlPluginRegistry;
diff --git a/payment/src/test/java/org/killbill/billing/payment/TestJanitor.java b/payment/src/test/java/org/killbill/billing/payment/TestJanitor.java
index 0fcfb7f..f6cd52a 100644
--- a/payment/src/test/java/org/killbill/billing/payment/TestJanitor.java
+++ b/payment/src/test/java/org/killbill/billing/payment/TestJanitor.java
@@ -496,7 +496,7 @@ public class TestJanitor extends PaymentTestSuiteWithEmbeddedDB {
(BigDecimal) row.get("amount"),
Currency.valueOf((String) row.get("currency")),
(String) row.get("gateway_error_code"),
- (String) row.get("gateway_error_msg"));
+ String.valueOf(row.get("gateway_error_msg")));
result.add(transactionModelDao);
}
return result;
diff --git a/profiles/killbill/src/main/java/org/killbill/billing/server/log/obfuscators/PatternObfuscator.java b/profiles/killbill/src/main/java/org/killbill/billing/server/log/obfuscators/PatternObfuscator.java
index bed40f9..05f7991 100644
--- a/profiles/killbill/src/main/java/org/killbill/billing/server/log/obfuscators/PatternObfuscator.java
+++ b/profiles/killbill/src/main/java/org/killbill/billing/server/log/obfuscators/PatternObfuscator.java
@@ -35,6 +35,11 @@ public class PatternObfuscator extends Obfuscator {
"bic",
"cardvalidationnum",
"cavv",
+ "ccFirstName",
+ "ccLastName",
+ "ccNumber",
+ "ccTrackData",
+ "ccVerificationValue",
"ccvv",
"cvNumber",
"cvc",
@@ -59,6 +64,8 @@ public class PatternObfuscator extends Obfuscator {
this.patterns.add(buildJSONPattern(sensitiveKey));
this.patterns.add(buildXMLPattern(sensitiveKey));
this.patterns.add(buildMultiValuesXMLPattern(sensitiveKey));
+ this.patterns.add(buildKeyValuePattern1(sensitiveKey));
+ this.patterns.add(buildKeyValuePattern2(sensitiveKey));
}
this.patterns.addAll(extraPatterns);
}
@@ -79,4 +86,14 @@ public class PatternObfuscator extends Obfuscator {
private Pattern buildMultiValuesXMLPattern(final String key) {
return Pattern.compile(key + "</key>\\s*<value[^>]*>([^<\\n]+)</value>", DEFAULT_PATTERN_FLAGS);
}
+
+ // Splunk-type logging
+ private Pattern buildKeyValuePattern1(final String key) {
+ return Pattern.compile(key + "\\s*=\\s*'([^\']+)'", DEFAULT_PATTERN_FLAGS);
+ }
+
+ // Splunk-type logging
+ private Pattern buildKeyValuePattern2(final String key) {
+ return Pattern.compile(key + "\\s*=\\s*\"([^\"]+)\"", DEFAULT_PATTERN_FLAGS);
+ }
}
diff --git a/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestCatalog.java b/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestCatalog.java
index 1485fb4..7aa8f80 100644
--- a/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestCatalog.java
+++ b/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestCatalog.java
@@ -79,7 +79,7 @@ public class TestCatalog extends TestJaxrsBase {
killBillClient.uploadXMLCatalog(versionPath1, requestOptions);
Assert.fail("Uploading same version should fail");
} catch (KillBillClientException e) {
- Assert.assertEquals(e.getMessage(), "Invalid catalog for tenant : 1");
+ Assert.assertTrue(e.getMessage().startsWith("Invalid catalog for tenant : "));
}
// Try to upload another version with an invalid name (different than orignal name)
@@ -88,7 +88,7 @@ public class TestCatalog extends TestJaxrsBase {
killBillClient.uploadXMLCatalog(versionPath2, requestOptions);
Assert.fail("Uploading same version should fail");
} catch (KillBillClientException e) {
- Assert.assertEquals(e.getMessage(), "Invalid catalog for tenant : 1");
+ Assert.assertTrue(e.getMessage().startsWith("Invalid catalog for tenant : "));
}
String catalog = killBillClient.getXMLCatalog(requestOptions);
diff --git a/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestInvoice.java b/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestInvoice.java
index d70c150..9ee931b 100644
--- a/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestInvoice.java
+++ b/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestInvoice.java
@@ -776,7 +776,7 @@ public class TestInvoice extends TestJaxrsBase {
credit.setCreditAmount(creditAmount);
// insert credit to child account
- final Credit creditJson = killBillClient.createCredit(credit, false, createdBy, reason, comment);
+ final Credit creditJson = killBillClient.createCredit(credit, true, requestOptions);
Invoices childInvoices = killBillClient.getInvoicesForAccount(childAccount.getAccountId(), true, false);
Assert.assertEquals(childInvoices.size(), 1);
diff --git a/profiles/killbill/src/test/java/org/killbill/billing/server/log/obfuscators/TestPatternObfuscator.java b/profiles/killbill/src/test/java/org/killbill/billing/server/log/obfuscators/TestPatternObfuscator.java
index 11d9162..c50ae34 100644
--- a/profiles/killbill/src/test/java/org/killbill/billing/server/log/obfuscators/TestPatternObfuscator.java
+++ b/profiles/killbill/src/test/java/org/killbill/billing/server/log/obfuscators/TestPatternObfuscator.java
@@ -278,6 +278,12 @@ public class TestPatternObfuscator extends ServerTestSuiteNoDB {
event);
}
+ @Test(groups = "fast")
+ public void testPluginProperties() throws Exception {
+ verify("ENTERING onSuccessCall paymentMethodId='e92a3bfd-0713-4396-a1e2-ff46cb051f8c' ccVerificationValue='123' ccNumber = '4111111111111111' ccTrackData=\"XXX\" ccFirstName = \"John\" ccLastName=\"'Smith'\"",
+ "ENTERING onSuccessCall paymentMethodId='e92a3bfd-0713-4396-a1e2-ff46cb051f8c' ccVerificationValue='***' ccNumber = '****************' ccTrackData=\"***\" ccFirstName = \"****\" ccLastName=\"*******\"");
+ }
+
private void verify(final String input, final ILoggingEvent event) {
verify(input, input, event);
}
diff --git a/subscription/src/main/java/org/killbill/billing/subscription/engine/dao/model/SubscriptionBundleModelDao.java b/subscription/src/main/java/org/killbill/billing/subscription/engine/dao/model/SubscriptionBundleModelDao.java
index 2f36acb..2c9489f 100644
--- a/subscription/src/main/java/org/killbill/billing/subscription/engine/dao/model/SubscriptionBundleModelDao.java
+++ b/subscription/src/main/java/org/killbill/billing/subscription/engine/dao/model/SubscriptionBundleModelDao.java
@@ -25,7 +25,7 @@ import org.killbill.billing.util.dao.TableName;
import org.killbill.billing.util.entity.dao.EntityModelDao;
import org.killbill.billing.util.entity.dao.EntityModelDaoBase;
-import com.google.common.base.Objects;
+import com.google.common.base.MoreObjects;
public class SubscriptionBundleModelDao extends EntityModelDaoBase implements EntityModelDao<SubscriptionBaseBundle> {
@@ -39,7 +39,7 @@ public class SubscriptionBundleModelDao extends EntityModelDaoBase implements En
public SubscriptionBundleModelDao(final UUID id, final String key, final UUID accountId, final DateTime lastSysUpdateDate,
final DateTime createdDate, DateTime originalCreatedDate, final DateTime updateDate) {
super(id, createdDate, updateDate);
- this.externalKey = Objects.firstNonNull(key, id.toString());
+ this.externalKey = MoreObjects.firstNonNull(key, id.toString());
this.accountId = accountId;
this.lastSysUpdateDate = lastSysUpdateDate;
this.originalCreatedDate = originalCreatedDate;
diff --git a/util/src/main/java/org/killbill/billing/util/audit/DefaultAccountAuditLogs.java b/util/src/main/java/org/killbill/billing/util/audit/DefaultAccountAuditLogs.java
index 87c8433..ae09fd2 100644
--- a/util/src/main/java/org/killbill/billing/util/audit/DefaultAccountAuditLogs.java
+++ b/util/src/main/java/org/killbill/billing/util/audit/DefaultAccountAuditLogs.java
@@ -29,7 +29,7 @@ import org.killbill.billing.util.api.AuditLevel;
import com.google.common.base.Predicate;
import com.google.common.collect.AbstractIterator;
import com.google.common.collect.ImmutableList;
-import com.google.common.collect.Iterators;
+import com.google.common.collect.ImmutableSet;
public class DefaultAccountAuditLogs implements AccountAuditLogs {
@@ -40,7 +40,7 @@ public class DefaultAccountAuditLogs implements AccountAuditLogs {
private final Map<ObjectType, DefaultAccountAuditLogsForObjectType> auditLogsCache = new HashMap<ObjectType, DefaultAccountAuditLogsForObjectType>();
public DefaultAccountAuditLogs(final UUID accountId) {
- this(accountId, AuditLevel.NONE, Iterators.<AuditLog>emptyIterator());
+ this(accountId, AuditLevel.NONE, ImmutableSet.<AuditLog>of().iterator());
}
public DefaultAccountAuditLogs(final UUID accountId, final AuditLevel auditLevel, final Iterator<AuditLog> accountAuditLogsOrderedByTableName) {
diff --git a/util/src/main/java/org/killbill/billing/util/audit/DefaultAccountAuditLogsForObjectType.java b/util/src/main/java/org/killbill/billing/util/audit/DefaultAccountAuditLogsForObjectType.java
index e8441c1..fbd7b37 100644
--- a/util/src/main/java/org/killbill/billing/util/audit/DefaultAccountAuditLogsForObjectType.java
+++ b/util/src/main/java/org/killbill/billing/util/audit/DefaultAccountAuditLogsForObjectType.java
@@ -28,6 +28,7 @@ import org.killbill.billing.util.customfield.ShouldntHappenException;
import com.google.common.base.Predicate;
import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterators;
public class DefaultAccountAuditLogsForObjectType implements AccountAuditLogsForObjectType {
@@ -38,7 +39,7 @@ public class DefaultAccountAuditLogsForObjectType implements AccountAuditLogsFor
private final Iterator<AuditLog> allAuditLogsForObjectType;
public DefaultAccountAuditLogsForObjectType(final AuditLevel auditLevel) {
- this(auditLevel, Iterators.<AuditLog>emptyIterator());
+ this(auditLevel, ImmutableSet.<AuditLog>of().iterator());
}
public DefaultAccountAuditLogsForObjectType(final AuditLevel auditLevel, final Iterator<AuditLog> allAuditLogsForObjectType) {
diff --git a/util/src/main/java/org/killbill/billing/util/entity/dao/EntitySqlDaoWrapperInvocationHandler.java b/util/src/main/java/org/killbill/billing/util/entity/dao/EntitySqlDaoWrapperInvocationHandler.java
index dde8ec7..872ba10 100644
--- a/util/src/main/java/org/killbill/billing/util/entity/dao/EntitySqlDaoWrapperInvocationHandler.java
+++ b/util/src/main/java/org/killbill/billing/util/entity/dao/EntitySqlDaoWrapperInvocationHandler.java
@@ -65,7 +65,7 @@ import org.skife.jdbi.v2.sqlobject.SqlObjectBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import com.google.common.base.Objects;
+import com.google.common.base.MoreObjects;
import com.google.common.base.Predicate;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableList.Builder;
@@ -375,8 +375,8 @@ public class EntitySqlDaoWrapperInvocationHandler<S extends EntitySqlDao<M, E>,
public Object execute() {
final M reHydratedEntity = sqlDao.getById(entityId, context);
final Long reHydratedEntityRecordId = sqlDao.getRecordId(entityId, context);
- final M entity = Objects.firstNonNull(reHydratedEntity, entities.get(entityId));
- final Long entityRecordId = Objects.firstNonNull(reHydratedEntityRecordId, entityRecordIds.get(entityId));
+ final M entity = MoreObjects.firstNonNull(reHydratedEntity, entities.get(entityId));
+ final Long entityRecordId = MoreObjects.firstNonNull(reHydratedEntityRecordId, entityRecordIds.get(entityId));
final TableName tableName = entity.getTableName();
// Note: audit entries point to the history record id
@@ -463,7 +463,7 @@ public class EntitySqlDaoWrapperInvocationHandler<S extends EntitySqlDao<M, E>,
}
private void insertAudits(final TableName tableName, final Long entityRecordId, final Long historyRecordId, final ChangeType changeType, final InternalCallContext contextMaybeWithoutAccountRecordId) {
- final TableName destinationTableName = Objects.firstNonNull(tableName.getHistoryTableName(), tableName);
+ final TableName destinationTableName = MoreObjects.firstNonNull(tableName.getHistoryTableName(), tableName);
final EntityAudit audit = new EntityAudit(destinationTableName, historyRecordId, changeType, clock.getUTCNow());
final InternalCallContext context;
diff --git a/util/src/test/java/org/killbill/billing/util/audit/dao/MockAuditDao.java b/util/src/test/java/org/killbill/billing/util/audit/dao/MockAuditDao.java
index f1cb189..1aa816d 100644
--- a/util/src/test/java/org/killbill/billing/util/audit/dao/MockAuditDao.java
+++ b/util/src/test/java/org/killbill/billing/util/audit/dao/MockAuditDao.java
@@ -29,7 +29,7 @@ import org.killbill.billing.util.audit.DefaultAccountAuditLogs;
import org.killbill.billing.util.audit.DefaultAccountAuditLogsForObjectType;
import org.killbill.billing.util.dao.TableName;
-import com.google.common.base.Objects;
+import com.google.common.base.MoreObjects;
import com.google.common.collect.ImmutableList;
public class MockAuditDao implements AuditDao {
@@ -70,7 +70,7 @@ public class MockAuditDao implements AuditDao {
}
final List<AuditLog> auditLogsForObjectId = auditLogsForTableName.get(objectId);
- final List<AuditLog> allAuditLogs = Objects.firstNonNull(auditLogsForObjectId, ImmutableList.<AuditLog>of());
+ final List<AuditLog> allAuditLogs = MoreObjects.firstNonNull(auditLogsForObjectId, ImmutableList.<AuditLog>of());
if (AuditLevel.FULL.equals(auditLevel)) {
return allAuditLogs;
} else if (AuditLevel.MINIMAL.equals(auditLevel) && allAuditLogs.size() > 0) {