killbill-uncached

Details

diff --git a/invoice/src/main/java/org/killbill/billing/invoice/calculator/InvoiceCalculatorUtils.java b/invoice/src/main/java/org/killbill/billing/invoice/calculator/InvoiceCalculatorUtils.java
index e815d1f..1437745 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/calculator/InvoiceCalculatorUtils.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/calculator/InvoiceCalculatorUtils.java
@@ -140,7 +140,7 @@ public abstract class InvoiceCalculatorUtils {
 
         for (final InvoiceItem invoiceItem : invoiceItems) {
             if (isCharge(invoiceItem) &&
-                invoiceItem.getCreatedDate().equals(invoiceCreatedDate)) {
+                (invoiceItem.getCreatedDate() != null && invoiceItem.getCreatedDate().equals(invoiceCreatedDate))) {
                 amountCharged = amountCharged.add(invoiceItem.getAmount());
             }
         }
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/model/InvoiceItemFactory.java b/invoice/src/main/java/org/killbill/billing/invoice/model/InvoiceItemFactory.java
index 7eeda4b..77fd9e3 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/model/InvoiceItemFactory.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/model/InvoiceItemFactory.java
@@ -23,7 +23,6 @@ import java.util.UUID;
 
 import org.joda.time.DateTime;
 import org.joda.time.LocalDate;
-
 import org.killbill.billing.catalog.api.Currency;
 import org.killbill.billing.invoice.api.InvoiceItem;
 import org.killbill.billing.invoice.api.InvoiceItemType;
@@ -86,7 +85,7 @@ public class InvoiceItemFactory {
                 item = new UsageInvoiceItem(id, createdDate, invoiceId, accountId, bundleId, subscriptionId, planName, phaseName, usageName, startDate, endDate, description, amount, currency);
                 break;
             case TAX:
-                item = new TaxInvoiceItem(id, createdDate, invoiceId, accountId, bundleId, description, startDate, amount, currency);
+                item = new TaxInvoiceItem(id, createdDate, invoiceId, accountId, bundleId, subscriptionId, planName, phaseName, usageName, startDate, description, amount, currency, linkedItemId);
                 break;
             default:
                 throw new RuntimeException("Unexpected type of event item " + type);
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/model/TaxInvoiceItem.java b/invoice/src/main/java/org/killbill/billing/invoice/model/TaxInvoiceItem.java
index 24fb389..16d0970 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/model/TaxInvoiceItem.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/model/TaxInvoiceItem.java
@@ -35,12 +35,13 @@ public class TaxInvoiceItem extends InvoiceItemBase {
 
     public TaxInvoiceItem(final UUID id, final UUID invoiceId, final UUID accountId, @Nullable final UUID bundleId,
                           @Nullable final String description, final LocalDate date, final BigDecimal amount, final Currency currency) {
-        this(id, null, invoiceId, accountId, bundleId, description, date, amount, currency);
+        this(id, null, invoiceId, accountId, bundleId, null, null, null, null, date, description, amount, currency, null);
     }
 
     public TaxInvoiceItem(final UUID id, @Nullable final DateTime createdDate, final UUID invoiceId, final UUID accountId, @Nullable final UUID bundleId,
-                          @Nullable final String description, final LocalDate date, final BigDecimal amount, final Currency currency) {
-        super(id, createdDate, invoiceId, accountId, bundleId, null, description, null, null, null, date, null, amount, currency);
+                          @Nullable final UUID subscriptionId, @Nullable final String planName, @Nullable final String phaseName, @Nullable final String usageName,
+                          final LocalDate date, @Nullable final String description, final BigDecimal amount, final Currency currency, @Nullable final UUID linkedItemId) {
+        super(id, createdDate, invoiceId, accountId, bundleId, subscriptionId, description, planName, phaseName, usageName, date, null, amount, currency, linkedItemId);
     }
 
     @Override

NEWS 15(+15 -0)

diff --git a/NEWS b/NEWS
index e34c46a..ff90da8 100644
--- a/NEWS
+++ b/NEWS
@@ -1,3 +1,18 @@
+0.13.1
+   https://github.com/killbill/killbill/issues/226
+   https://github.com/killbill/killbill/issues/233
+   https://github.com/killbill/killbill/issues/234
+   https://github.com/killbill/killbill/issues/235
+   https://github.com/killbill/killbill/issues/236
+   https://github.com/killbill/killbill/issues/245
+   Add support for invoice plugins
+   Export more fields in TaxInvoiceItem
+   Update killbill-oss-parent to 0.9.7
+
+0.12.2
+   Fix state machine flow for PENDING payments
+   Fix Guice wiring in JAX-RS
+
 0.12.1
    Fix NPE in case of aborted payments
    Add missing headers for CORS
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 ffbb208..e01c8ab 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
@@ -17,14 +17,10 @@
 
 package org.killbill.billing.payment.core;
 
-import java.util.UUID;
 import java.util.concurrent.Callable;
-import java.util.concurrent.ExecutionException;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.TimeUnit;
-import java.util.concurrent.TimeoutException;
 
-import javax.annotation.Nullable;
 import javax.inject.Inject;
 
 import org.killbill.billing.ErrorCode;
@@ -47,10 +43,8 @@ import org.killbill.billing.util.cache.CacheControllerDispatcher;
 import org.killbill.billing.util.callcontext.CallContext;
 import org.killbill.billing.util.config.PaymentConfig;
 import org.killbill.billing.util.dao.NonEntityDao;
-import org.killbill.bus.api.PersistentBus;
 import org.killbill.clock.Clock;
 import org.killbill.commons.locker.GlobalLocker;
-import org.killbill.commons.locker.LockFailedException;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -59,6 +53,11 @@ import com.google.inject.name.Named;
 
 import static org.killbill.billing.payment.glue.PaymentModule.PLUGIN_EXECUTOR_NAMED;
 
+// We don't take any lock here because the call needs to be re-entrant
+// from the plugin: for example, the BitPay plugin will create the payment during the
+// processNotification call, while the PayU plugin will create it during buildFormDescriptor.
+// These calls are not necessarily idempotent though (the PayU plugin will create
+// a voucher in the gateway during the buildFormDescriptor call).
 public class PaymentGatewayProcessor extends ProcessorBase {
 
     private final PluginDispatcher<HostedPaymentPageFormDescriptor> paymentPluginFormDispatcher;
@@ -93,35 +92,29 @@ public class PaymentGatewayProcessor extends ProcessorBase {
                                                      try {
                                                          final GatewayNotification result = plugin.processNotification(notification, properties, callContext);
                                                          return PluginDispatcher.createPluginDispatcherReturnType(result);
-                                                     } catch (PaymentPluginApiException e) {
+                                                     } catch (final PaymentPluginApiException e) {
                                                          throw new PaymentApiException(ErrorCode.PAYMENT_PLUGIN_EXCEPTION, e.getErrorMessage());
                                                      }
                                                  }
                                              }, paymentPluginNotificationDispatcher);
     }
 
-
-
     public HostedPaymentPageFormDescriptor buildFormDescriptor(final Account account, final Iterable<PluginProperty> customFields, final Iterable<PluginProperty> properties, final CallContext callContext, final InternalCallContext internalCallContext) throws PaymentApiException {
         return dispatchWithExceptionHandling(account,
-                                             new CallableWithAccountLock<HostedPaymentPageFormDescriptor, PaymentApiException>(locker,
-                                                                                                                               account.getExternalKey(),
-                                                                                                                               new WithAccountLockCallback<PluginDispatcherReturnType<HostedPaymentPageFormDescriptor>, PaymentApiException>() {
-                                                                                                                                   @Override
-                                                                                                                                   public PluginDispatcherReturnType<HostedPaymentPageFormDescriptor> doOperation() throws PaymentApiException {
-                                                                                                                                       final PaymentPluginApi plugin = getPaymentProviderPlugin(account, internalCallContext);
+                                             new Callable<PluginDispatcherReturnType<HostedPaymentPageFormDescriptor>>() {
+                                                 @Override
+                                                 public PluginDispatcherReturnType<HostedPaymentPageFormDescriptor> call() throws PaymentApiException {
+                                                     final PaymentPluginApi plugin = getPaymentProviderPlugin(account, internalCallContext);
 
-                                                                                                                                       try {
-                                                                                                                                           final HostedPaymentPageFormDescriptor result = plugin.buildFormDescriptor(account.getId(), customFields, properties, callContext);
-                                                                                                                                           return PluginDispatcher.createPluginDispatcherReturnType(result);
-                                                                                                                                       } catch (final RuntimeException e) {
-                                                                                                                                           throw new PaymentApiException(e, ErrorCode.PAYMENT_INTERNAL_ERROR, Objects.firstNonNull(e.getMessage(), ""));
-                                                                                                                                       } catch (final PaymentPluginApiException e) {
-                                                                                                                                           throw new PaymentApiException(ErrorCode.PAYMENT_PLUGIN_EXCEPTION, e.getErrorMessage());
-                                                                                                                                       }
-                                                                                                                                   }
-                                                                                                                               }),
-                                             paymentPluginFormDispatcher);
+                                                     try {
+                                                         final HostedPaymentPageFormDescriptor result = plugin.buildFormDescriptor(account.getId(), customFields, properties, callContext);
+                                                         return PluginDispatcher.createPluginDispatcherReturnType(result);
+                                                     } catch (final RuntimeException e) {
+                                                         throw new PaymentApiException(e, ErrorCode.PAYMENT_INTERNAL_ERROR, Objects.firstNonNull(e.getMessage(), ""));
+                                                     } catch (final PaymentPluginApiException e) {
+                                                         throw new PaymentApiException(ErrorCode.PAYMENT_PLUGIN_EXCEPTION, e.getErrorMessage());
+                                                     }
+                                                 }
+                                             }, paymentPluginFormDispatcher);
     }
-
 }
diff --git a/payment/src/main/resources/org/killbill/billing/payment/PaymentStates.xml b/payment/src/main/resources/org/killbill/billing/payment/PaymentStates.xml
index f4aeef9..fa8ccf3 100644
--- a/payment/src/main/resources/org/killbill/billing/payment/PaymentStates.xml
+++ b/payment/src/main/resources/org/killbill/billing/payment/PaymentStates.xml
@@ -127,6 +127,7 @@
         <stateMachine name="PURCHASE">
             <states>
                 <state name="PURCHASE_INIT"/>
+                <state name="PURCHASE_PENDING"/>
                 <state name="PURCHASE_SUCCESS"/>
                 <state name="PURCHASE_FAILED"/>
                 <state name="PURCHASE_ERRORED"/>
@@ -147,6 +148,30 @@
                 <transition>
                     <initialState>PURCHASE_INIT</initialState>
                     <operation>OP_PURCHASE</operation>
+                    <operationResult>PENDING</operationResult>
+                    <finalState>PURCHASE_PENDING</finalState>
+                </transition>
+                <transition>
+                    <initialState>PURCHASE_PENDING</initialState>
+                    <operation>OP_PURCHASE</operation>
+                    <operationResult>SUCCESS</operationResult>
+                    <finalState>PURCHASE_SUCCESS</finalState>
+                </transition>
+                <transition>
+                    <initialState>PURCHASE_PENDING</initialState>
+                    <operation>OP_PURCHASE</operation>
+                    <operationResult>FAILURE</operationResult>
+                    <finalState>PURCHASE_FAILED</finalState>
+                </transition>
+                <transition>
+                    <initialState>PURCHASE_PENDING</initialState>
+                    <operation>OP_PURCHASE</operation>
+                    <operationResult>EXCEPTION</operationResult>
+                    <finalState>PURCHASE_ERRORED</finalState>
+                </transition>
+                <transition>
+                    <initialState>PURCHASE_INIT</initialState>
+                    <operation>OP_PURCHASE</operation>
                     <operationResult>EXCEPTION</operationResult>
                     <finalState>PURCHASE_ERRORED</finalState>
                 </transition>

pom.xml 2(+1 -1)

diff --git a/pom.xml b/pom.xml
index de67363..0b1436a 100644
--- a/pom.xml
+++ b/pom.xml
@@ -20,7 +20,7 @@
     <parent>
         <artifactId>killbill-oss-parent</artifactId>
         <groupId>org.kill-bill.billing</groupId>
-        <version>0.9.5</version>
+        <version>0.9.7</version>
     </parent>
     <artifactId>killbill</artifactId>
     <version>0.13.1-SNAPSHOT</version>
diff --git a/profiles/killbill/src/main/java/org/killbill/billing/server/security/TenantFilter.java b/profiles/killbill/src/main/java/org/killbill/billing/server/security/TenantFilter.java
index c254cd0..29dc32c 100644
--- a/profiles/killbill/src/main/java/org/killbill/billing/server/security/TenantFilter.java
+++ b/profiles/killbill/src/main/java/org/killbill/billing/server/security/TenantFilter.java
@@ -125,17 +125,19 @@ public class TenantFilter implements Filter {
 
         if (request instanceof HttpServletRequest) {
             final HttpServletRequest httpServletRequest = (HttpServletRequest) request;
+            // TODO Wrong - See https://github.com/killbill/killbill/issues/221
             final String path = httpServletRequest.getRequestURI();
+            final String httpMethod = httpServletRequest.getMethod();
             if (    // Chicken - egg problem
-                    ("/1.0/kb/tenants".equals(path) && "POST".equals(httpServletRequest.getMethod())) ||
+                    isTenantCreationRequest(path, httpMethod) ||
                     // Retrieve user permissions should not require tenant info since this is cross tenants
-                    (("/1.0/kb/security/subject".equals(path) || "/1.0/kb/security/permissions".equals(path)) && "GET".equals(httpServletRequest.getMethod())) ||
+                    isPermissionRequest(path, httpMethod) ||
                     // Metrics servlets
-                    (KillbillGuiceListener.METRICS_SERVLETS_PATHS.contains(path) && "GET".equals(httpServletRequest.getMethod())) ||
+                    isMetricsRequest(path, httpMethod) ||
                     // See KillBillShiroWebModule#CorsBasicHttpAuthenticationFilter
-                    "OPTIONS".equals(httpServletRequest.getMethod()) ||
-                    // Welcome screen, static resources, etc.
-                    (!path.startsWith("/1.0") && "GET".equals(httpServletRequest.getMethod()))
+                    isOptionsRequest(httpMethod) ||
+                    // Static resources
+                    isStaticResourceRequest(path, httpMethod)
                     ) {
                 shouldSkip = true;
             }
@@ -144,6 +146,53 @@ public class TenantFilter implements Filter {
         return shouldSkip;
     }
 
+    private boolean isPermissionRequest(final String path, final String httpMethod) {
+        return JaxrsResource.SECURITY_PATH.startsWith(path) && "GET".equals(httpMethod);
+    }
+
+    private boolean isTenantCreationRequest(final String path, final String httpMethod) {
+        return JaxrsResource.TENANTS_PATH.equals(path) && "POST".equals(httpMethod);
+    }
+
+    private boolean isMetricsRequest(final String path, final String httpMethod) {
+        return KillbillGuiceListener.METRICS_SERVLETS_PATHS.contains(path) && "GET".equals(httpMethod);
+    }
+
+    private boolean isOptionsRequest(final String httpMethod) {
+        return "OPTIONS".equals(httpMethod);
+    }
+
+    private boolean isStaticResourceRequest(final String path, final String httpMethod) {
+        if (isPluginRequest(path)) {
+            // For plugins requests, we want to validate the Tenant except for HTML, JS, etc. files
+            return isStaticFileRequest(path) && "GET".equals(httpMethod);
+        } else {
+            // Welcome screen, Swagger, etc.
+            return !isKbApiRequest(path) && "GET".equals(httpMethod);
+        }
+    }
+
+    private boolean isKbApiRequest(final String path) {
+        return path.startsWith(JaxrsResource.PREFIX);
+    }
+
+    private boolean isPluginRequest(final String path) {
+        return path.startsWith(JaxrsResource.PLUGINS_PATH);
+    }
+
+    private boolean isStaticFileRequest(final String path) {
+        return path.endsWith(".htm") ||
+               path.endsWith(".html") ||
+               path.endsWith(".js") ||
+               path.endsWith(".css") ||
+               path.endsWith(".gz") ||
+               path.endsWith(".xml") ||
+               path.endsWith(".txt") ||
+               path.endsWith(".map")||
+               path.endsWith(".woff")||
+               path.endsWith(".ttf");
+    }
+
     private void sendAuthError(final ServletResponse response, final String errorMessage) throws IOException {
         if (response instanceof HttpServletResponse) {
             final HttpServletResponse httpServletResponse = (HttpServletResponse) response;