killbill-memoizeit

Implement adjusting plugin properties for all control plugin

8/5/2015 7:51:54 PM

Details

diff --git a/payment/src/main/java/org/killbill/billing/payment/core/sm/control/CompletionControlOperation.java b/payment/src/main/java/org/killbill/billing/payment/core/sm/control/CompletionControlOperation.java
index 09b1231..d6369e7 100644
--- a/payment/src/main/java/org/killbill/billing/payment/core/sm/control/CompletionControlOperation.java
+++ b/payment/src/main/java/org/killbill/billing/payment/core/sm/control/CompletionControlOperation.java
@@ -59,7 +59,6 @@ public class CompletionControlOperation extends OperationControlCallback {
                                                                                                             transaction.getCurrency(),
                                                                                                             transaction.getProcessedAmount(),
                                                                                                             transaction.getProcessedCurrency(),
-                                                                                                            paymentStateContext.getProperties(),
                                                                                                             paymentStateControlContext.isApiPayment(),
                                                                                                             paymentStateContext.getCallContext());
 
diff --git a/payment/src/main/java/org/killbill/billing/payment/core/sm/control/OperationControlCallback.java b/payment/src/main/java/org/killbill/billing/payment/core/sm/control/OperationControlCallback.java
index d6ec560..1c2f87c 100644
--- a/payment/src/main/java/org/killbill/billing/payment/core/sm/control/OperationControlCallback.java
+++ b/payment/src/main/java/org/killbill/billing/payment/core/sm/control/OperationControlCallback.java
@@ -32,6 +32,7 @@ import org.killbill.automaton.OperationResult;
 import org.killbill.billing.account.api.Account;
 import org.killbill.billing.callcontext.DefaultCallContext;
 import org.killbill.billing.catalog.api.Currency;
+import org.killbill.billing.control.plugin.api.OnSuccessPaymentControlResult;
 import org.killbill.billing.osgi.api.OSGIServiceRegistration;
 import org.killbill.billing.payment.api.Payment;
 import org.killbill.billing.payment.api.PaymentApiException;
@@ -50,6 +51,7 @@ 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.payment.retry.DefaultPriorPaymentControlResult;
 import org.killbill.billing.util.callcontext.CallContext;
 import org.killbill.commons.locker.GlobalLocker;
 import org.killbill.commons.locker.LockFailedException;
@@ -92,7 +94,6 @@ public abstract class OperationControlCallback extends OperationCallbackBase<Pay
                                                                                                      paymentStateContext.getTransactionType(),
                                                                                                      paymentStateContext.getAmount(),
                                                                                                      paymentStateContext.getCurrency(),
-                                                                                                     paymentStateContext.getProperties(),
                                                                                                      paymentStateControlContext.isApiPayment(),
                                                                                                      paymentStateContext.getCallContext());
 
@@ -110,9 +111,6 @@ public abstract class OperationControlCallback extends OperationCallbackBase<Pay
 
                 final boolean success;
                 try {
-                    // Adjust amount with value returned by plugin if necessary
-                    adjustStateContextValues(paymentStateContext, pluginResult);
-
                     final Payment result = doCallSpecificOperationCallback();
                     ((PaymentStateControlContext) paymentStateContext).setResult(result);
                     final PaymentTransaction transaction = ((PaymentStateControlContext) paymentStateContext).getCurrentTransaction();
@@ -131,7 +129,6 @@ public abstract class OperationControlCallback extends OperationCallbackBase<Pay
                                                                                                                     transaction.getCurrency(),
                                                                                                                     transaction.getProcessedAmount(),
                                                                                                                     transaction.getProcessedCurrency(),
-                                                                                                                    paymentStateContext.getProperties(),
                                                                                                                     paymentStateControlContext.isApiPayment(),
                                                                                                                     paymentStateContext.getCallContext());
 
@@ -173,40 +170,6 @@ public abstract class OperationControlCallback extends OperationCallbackBase<Pay
         return new OperationException(e, getOperationResultOnException(paymentStateContext));
     }
 
-
-    protected void executePluginOnSuccessCalls(final List<String> paymentControlPluginNames, final PaymentControlContext paymentControlContext) {
-        for (final String pluginName : paymentControlPluginNames) {
-            final PaymentControlPluginApi plugin = paymentControlPluginRegistry.getServiceForName(pluginName);
-            if (plugin != null) {
-                try {
-                    plugin.onSuccessCall(paymentControlContext, paymentStateContext.getProperties());
-                } catch (final PaymentControlApiException e) {
-                    logger.warn("Plugin " + pluginName + " failed to complete executePluginOnSuccessCalls call for " + paymentControlContext.getPaymentExternalKey(), e);
-                }
-            }
-        }
-    }
-
-    private void adjustStateContextValues(final PaymentStateContext inputContext, @Nullable final PriorPaymentControlResult pluginResult) {
-        if (pluginResult == null) {
-            return;
-        }
-
-        final PaymentStateControlContext input = (PaymentStateControlContext) inputContext;
-        if (pluginResult.getAdjustedAmount() != null) {
-            input.setAmount(pluginResult.getAdjustedAmount());
-        }
-        if (pluginResult.getAdjustedCurrency() != null) {
-            input.setCurrency(pluginResult.getAdjustedCurrency());
-        }
-        if (pluginResult.getAdjustedPaymentMethodId() != null) {
-            input.setPaymentMethodId(pluginResult.getAdjustedPaymentMethodId());
-        }
-        if (pluginResult.getAdjustedPluginProperties() != null) {
-            input.setProperties(pluginResult.getAdjustedPluginProperties());
-        }
-    }
-
     private OperationResult getOperationResultOnException(final PaymentStateContext paymentStateContext) {
         final PaymentStateControlContext paymentStateControlContext = (PaymentStateControlContext) paymentStateContext;
         final OperationResult operationResult = paymentStateControlContext.getRetryDate() != null ? OperationResult.FAILURE : OperationResult.EXCEPTION;
@@ -217,7 +180,9 @@ public abstract class OperationControlCallback extends OperationCallbackBase<Pay
         // Return as soon as the first plugin aborts, or the last result for the last plugin
         PriorPaymentControlResult prevResult = null;
 
+        // Those values are adjusted prior each call with the result of what previous call to plugin returned
         PaymentControlContext inputPaymentControlContext = paymentControlContextArg;
+        Iterable<PluginProperty> inputPluginProperties = paymentStateContext.getProperties();
 
         for (final String pluginName : paymentControlPluginNames) {
             final PaymentControlPluginApi plugin = paymentControlPluginRegistry.getServiceForName(pluginName);
@@ -226,7 +191,10 @@ public abstract class OperationControlCallback extends OperationCallbackBase<Pay
                 logger.warn("Skipping unknown payment control plugin {} when fetching results", pluginName);
                 continue;
             }
-            prevResult = plugin.priorCall(inputPaymentControlContext, paymentStateContext.getProperties());
+            prevResult = plugin.priorCall(inputPaymentControlContext, inputPluginProperties);
+            if (prevResult.getAdjustedPluginProperties() != null) {
+                inputPluginProperties = prevResult.getAdjustedPluginProperties();
+            }
             if (prevResult.isAborted()) {
                 break;
             }
@@ -239,14 +207,36 @@ public abstract class OperationControlCallback extends OperationCallbackBase<Pay
                                                                           paymentStateContext.getTransactionType(),
                                                                           prevResult.getAdjustedAmount() != null ? prevResult.getAdjustedAmount() : inputPaymentControlContext.getAmount(),
                                                                           prevResult.getAdjustedCurrency() != null ? prevResult.getAdjustedCurrency() : inputPaymentControlContext.getCurrency(),
-                                                                          prevResult.getAdjustedPluginProperties() != null ? prevResult.getAdjustedPluginProperties() : inputPaymentControlContext.getPluginProperties(),
                                                                           paymentStateControlContext.isApiPayment(),
                                                                           paymentStateContext.getCallContext());
 
         }
+        // Rebuild latest result to include inputPluginProperties
+        prevResult = new DefaultPriorPaymentControlResult(prevResult, inputPluginProperties);
+        // Adjust context with all values if necessary
+        adjustStateContextForPriorCall(paymentStateContext, prevResult);
         return prevResult;
     }
 
+    protected void executePluginOnSuccessCalls(final List<String> paymentControlPluginNames, final PaymentControlContext paymentControlContext) {
+
+        Iterable<PluginProperty> inputPluginProperties = paymentStateContext.getProperties();
+        for (final String pluginName : paymentControlPluginNames) {
+            final PaymentControlPluginApi plugin = paymentControlPluginRegistry.getServiceForName(pluginName);
+            if (plugin != null) {
+                try {
+                    final OnSuccessPaymentControlResult result = plugin.onSuccessCall(paymentControlContext, inputPluginProperties);
+                    if (result.getAdjustedPluginProperties() != null) {
+                        inputPluginProperties = result.getAdjustedPluginProperties();
+                    }
+                } catch (final PaymentControlApiException e) {
+                    logger.warn("Plugin " + pluginName + " failed to complete executePluginOnSuccessCalls call for " + paymentControlContext.getPaymentExternalKey(), e);
+                }
+            }
+        }
+        adjustStateContextPluginProperties(paymentStateContext, inputPluginProperties);
+    }
+
     private OperationResult executePluginOnFailureCallsAndSetRetryDate(final PaymentStateControlContext paymentStateControlContext, final PaymentControlContext paymentControlContext) {
         final DateTime retryDate = executePluginOnFailureCalls(paymentStateControlContext.getPaymentControlPluginNames(), paymentControlContext);
         if (retryDate != null) {
@@ -256,26 +246,61 @@ public abstract class OperationControlCallback extends OperationCallbackBase<Pay
     }
 
     private DateTime executePluginOnFailureCalls(final List<String> paymentControlPluginNames, final PaymentControlContext paymentControlContext) {
+
         DateTime candidate = null;
+        Iterable<PluginProperty> inputPluginProperties = paymentStateContext.getProperties();
+
         for (final String pluginName : paymentControlPluginNames) {
             final PaymentControlPluginApi plugin = paymentControlPluginRegistry.getServiceForName(pluginName);
             if (plugin != null) {
                 try {
-                    final OnFailurePaymentControlResult result = plugin.onFailureCall(paymentControlContext, paymentStateContext.getProperties());
+                    final OnFailurePaymentControlResult result = plugin.onFailureCall(paymentControlContext, inputPluginProperties);
                     if (candidate == null) {
                         candidate = result.getNextRetryDate();
                     } else if (result.getNextRetryDate() != null) {
                         candidate = candidate.compareTo(result.getNextRetryDate()) > 0 ? result.getNextRetryDate() : candidate;
                     }
+
+                    if (result.getAdjustedPluginProperties() != null) {
+                        inputPluginProperties = result.getAdjustedPluginProperties();
+                    }
+
                 } catch (final PaymentControlApiException e) {
                     logger.warn("Plugin " + pluginName + " failed to return next retryDate for payment " + paymentControlContext.getPaymentExternalKey(), e);
                     return candidate;
                 }
             }
         }
+        adjustStateContextPluginProperties(paymentStateContext, inputPluginProperties);
         return candidate;
     }
 
+    private void adjustStateContextForPriorCall(final PaymentStateContext inputContext, @Nullable final PriorPaymentControlResult pluginResult) {
+        if (pluginResult == null) {
+            return;
+        }
+
+        final PaymentStateControlContext input = (PaymentStateControlContext) inputContext;
+        if (pluginResult.getAdjustedAmount() != null) {
+            input.setAmount(pluginResult.getAdjustedAmount());
+        }
+        if (pluginResult.getAdjustedCurrency() != null) {
+            input.setCurrency(pluginResult.getAdjustedCurrency());
+        }
+        if (pluginResult.getAdjustedPaymentMethodId() != null) {
+            input.setPaymentMethodId(pluginResult.getAdjustedPaymentMethodId());
+        }
+        adjustStateContextPluginProperties(inputContext, pluginResult.getAdjustedPluginProperties());
+    }
+
+    private void adjustStateContextPluginProperties(final PaymentStateContext inputContext, @Nullable Iterable<PluginProperty> pluginProperties) {
+        if (pluginProperties == null) {
+            return;
+        }
+        final PaymentStateControlContext input = (PaymentStateControlContext) inputContext;
+        input.setProperties(pluginProperties);
+    }
+
     public static class DefaultPaymentControlContext extends DefaultCallContext implements PaymentControlContext {
 
         private final Account account;
@@ -291,15 +316,14 @@ public abstract class OperationControlCallback extends OperationCallbackBase<Pay
         private final BigDecimal processedAmount;
         private final Currency processedCurrency;
         private final boolean isApiPayment;
-        private final Iterable<PluginProperty> properties;
 
         public DefaultPaymentControlContext(final Account account, final UUID paymentMethodId, final UUID attemptId, @Nullable final UUID paymentId, final String paymentExternalKey, final String transactionExternalKey, final TransactionType transactionType, final BigDecimal amount, final Currency currency,
-                                            final Iterable<PluginProperty> properties, final boolean isApiPayment, final CallContext callContext) {
-            this(account, paymentMethodId, attemptId, paymentId, paymentExternalKey, null, transactionExternalKey, transactionType, amount, currency, null, null, properties, isApiPayment, callContext);
+                                            final boolean isApiPayment, final CallContext callContext) {
+            this(account, paymentMethodId, attemptId, paymentId, paymentExternalKey, null, transactionExternalKey, transactionType, amount, currency, null, null, isApiPayment, callContext);
         }
 
         public DefaultPaymentControlContext(final Account account, final UUID paymentMethodId, final UUID attemptId, @Nullable final UUID paymentId, final String paymentExternalKey, @Nullable final UUID transactionId, final String transactionExternalKey, final TransactionType transactionType,
-                                            final BigDecimal amount, final Currency currency, @Nullable final BigDecimal processedAmount, @Nullable final Currency processedCurrency, final Iterable<PluginProperty> properties, final boolean isApiPayment, final CallContext callContext) {
+                                            final BigDecimal amount, final Currency currency, @Nullable final BigDecimal processedAmount, @Nullable final Currency processedCurrency, final boolean isApiPayment, final CallContext callContext) {
             super(callContext.getTenantId(), callContext.getUserName(), callContext.getCallOrigin(), callContext.getUserType(), callContext.getReasonCode(), callContext.getComments(), callContext.getUserToken(), callContext.getCreatedDate(), callContext.getUpdatedDate());
             this.account = account;
             this.paymentMethodId = paymentMethodId;
@@ -313,7 +337,6 @@ public abstract class OperationControlCallback extends OperationCallbackBase<Pay
             this.currency = currency;
             this.processedAmount = processedAmount;
             this.processedCurrency = processedCurrency;
-            this.properties = properties;
             this.isApiPayment = isApiPayment;
         }
 
@@ -382,11 +405,6 @@ public abstract class OperationControlCallback extends OperationCallbackBase<Pay
         }
 
         @Override
-        public Iterable<PluginProperty> getPluginProperties() {
-            return properties;
-        }
-
-        @Override
         public String toString() {
             return "DefaultPaymentControlContext{" +
                    "account=" + account +
@@ -402,7 +420,6 @@ public abstract class OperationControlCallback extends OperationCallbackBase<Pay
                    ", processedAmount=" + processedAmount +
                    ", processedCurrency=" + processedCurrency +
                    ", isApiPayment=" + isApiPayment +
-                   ", properties=" + properties +
                    '}';
         }
     }
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 848d7e2..760b2c9 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
@@ -112,7 +112,7 @@ public final class InvoicePaymentControlPluginApi implements PaymentControlPlugi
     }
 
     @Override
-    public PriorPaymentControlResult priorCall(final PaymentControlContext paymentControlContext, final Iterable<PluginProperty> properties) throws PaymentControlApiException {
+    public PriorPaymentControlResult priorCall(final PaymentControlContext paymentControlContext, final Iterable<PluginProperty> pluginProperties) throws PaymentControlApiException {
         final TransactionType transactionType = paymentControlContext.getTransactionType();
         Preconditions.checkArgument(transactionType == TransactionType.PURCHASE ||
                                     transactionType == TransactionType.REFUND ||
@@ -121,9 +121,9 @@ public final class InvoicePaymentControlPluginApi implements PaymentControlPlugi
         final InternalCallContext internalContext = internalCallContextFactory.createInternalCallContext(paymentControlContext.getAccountId(), paymentControlContext);
         switch (transactionType) {
             case PURCHASE:
-                return getPluginPurchaseResult(paymentControlContext, internalContext);
+                return getPluginPurchaseResult(paymentControlContext, pluginProperties, internalContext);
             case REFUND:
-                return getPluginRefundResult(paymentControlContext, internalContext);
+                return getPluginRefundResult(paymentControlContext, pluginProperties, internalContext);
             case CHARGEBACK:
                 return new DefaultPriorPaymentControlResult(false, paymentControlContext.getAmount());
             default:
@@ -132,7 +132,7 @@ public final class InvoicePaymentControlPluginApi implements PaymentControlPlugi
     }
 
     @Override
-    public OnSuccessPaymentControlResult onSuccessCall(final PaymentControlContext paymentControlContext, final Iterable<PluginProperty> properties) throws PaymentControlApiException {
+    public OnSuccessPaymentControlResult onSuccessCall(final PaymentControlContext paymentControlContext, final Iterable<PluginProperty> pluginProperties) throws PaymentControlApiException {
         final TransactionType transactionType = paymentControlContext.getTransactionType();
         Preconditions.checkArgument(transactionType == TransactionType.PURCHASE ||
                                     transactionType == TransactionType.REFUND ||
@@ -143,7 +143,7 @@ public final class InvoicePaymentControlPluginApi implements PaymentControlPlugi
             final InvoicePayment existingInvoicePayment;
             switch (transactionType) {
                 case PURCHASE:
-                    final UUID invoiceId = getInvoiceId(paymentControlContext);
+                    final UUID invoiceId = getInvoiceId(pluginProperties);
                     existingInvoicePayment = invoiceApi.getInvoicePaymentForAttempt(paymentControlContext.getPaymentId(), internalContext);
                     if (existingInvoicePayment != null) {
                         log.info("onSuccessCall was already completed for payment purchase :" + paymentControlContext.getPaymentId());
@@ -163,8 +163,8 @@ public final class InvoicePaymentControlPluginApi implements PaymentControlPlugi
                     if (existingInvoicePayment != null) {
                         log.info("onSuccessCall was already completed for payment refund :" + paymentControlContext.getPaymentId());
                     } else {
-                        final Map<UUID, BigDecimal> idWithAmount = extractIdsWithAmountFromProperties(paymentControlContext.getPluginProperties());
-                        final PluginProperty prop = getPluginProperty(paymentControlContext.getPluginProperties(), PROP_IPCD_REFUND_WITH_ADJUSTMENTS);
+                        final Map<UUID, BigDecimal> idWithAmount = extractIdsWithAmountFromProperties(pluginProperties);
+                        final PluginProperty prop = getPluginProperty(pluginProperties, PROP_IPCD_REFUND_WITH_ADJUSTMENTS);
                         final boolean isAdjusted = prop != null ? Boolean.valueOf((String) prop.getValue()) : false;
                         invoiceApi.createRefund(paymentControlContext.getPaymentId(), paymentControlContext.getAmount(), isAdjusted, idWithAmount, paymentControlContext.getTransactionExternalKey(), internalContext);
                     }
@@ -214,8 +214,8 @@ public final class InvoicePaymentControlPluginApi implements PaymentControlPlugi
         controlDao.removeAutoPayOffEntry(account.getId());
     }
 
-    private UUID getInvoiceId(final PaymentControlContext paymentControlContext) throws PaymentControlApiException {
-        final PluginProperty invoiceProp = getPluginProperty(paymentControlContext.getPluginProperties(), PROP_IPCD_INVOICE_ID);
+    private UUID getInvoiceId(final Iterable<PluginProperty> pluginProperties) throws PaymentControlApiException {
+        final PluginProperty invoiceProp = getPluginProperty(pluginProperties, PROP_IPCD_INVOICE_ID);
         if (invoiceProp == null ||
             !(invoiceProp.getValue() instanceof String)) {
             throw new PaymentControlApiException("Need to specify a valid invoiceId in property " + PROP_IPCD_INVOICE_ID);
@@ -223,9 +223,9 @@ public final class InvoicePaymentControlPluginApi implements PaymentControlPlugi
         return UUID.fromString((String) invoiceProp.getValue());
     }
 
-    private PriorPaymentControlResult getPluginPurchaseResult(final PaymentControlContext paymentControlPluginContext, final InternalCallContext internalContext) throws PaymentControlApiException {
+    private PriorPaymentControlResult getPluginPurchaseResult(final PaymentControlContext paymentControlPluginContext, final Iterable<PluginProperty> pluginProperties, final InternalCallContext internalContext) throws PaymentControlApiException {
         try {
-            final UUID invoiceId = getInvoiceId(paymentControlPluginContext);
+            final UUID invoiceId = getInvoiceId(pluginProperties);
             final Invoice invoice = rebalanceAndGetInvoice(invoiceId, internalContext);
             final BigDecimal requestedAmount = validateAndComputePaymentAmount(invoice, paymentControlPluginContext.getAmount(), paymentControlPluginContext.isApiPayment());
 
@@ -248,8 +248,8 @@ public final class InvoicePaymentControlPluginApi implements PaymentControlPlugi
         }
     }
 
-    private PriorPaymentControlResult getPluginRefundResult(final PaymentControlContext paymentControlPluginContext, final InternalCallContext internalContext) throws PaymentControlApiException {
-        final Map<UUID, BigDecimal> idWithAmount = extractIdsWithAmountFromProperties(paymentControlPluginContext.getPluginProperties());
+    private PriorPaymentControlResult getPluginRefundResult(final PaymentControlContext paymentControlPluginContext, final Iterable<PluginProperty> pluginProperties, final InternalCallContext internalContext) throws PaymentControlApiException {
+        final Map<UUID, BigDecimal> idWithAmount = extractIdsWithAmountFromProperties(pluginProperties);
         if ((paymentControlPluginContext.getAmount() == null || paymentControlPluginContext.getAmount().compareTo(BigDecimal.ZERO) == 0) &&
             idWithAmount.size() == 0) {
             throw new PaymentControlApiException("Refund for payment, key = " + paymentControlPluginContext.getPaymentExternalKey() +
diff --git a/payment/src/main/java/org/killbill/billing/payment/retry/DefaultPriorPaymentControlResult.java b/payment/src/main/java/org/killbill/billing/payment/retry/DefaultPriorPaymentControlResult.java
index 1cb5f14..df030b0 100644
--- a/payment/src/main/java/org/killbill/billing/payment/retry/DefaultPriorPaymentControlResult.java
+++ b/payment/src/main/java/org/killbill/billing/payment/retry/DefaultPriorPaymentControlResult.java
@@ -52,6 +52,11 @@ public class DefaultPriorPaymentControlResult implements PriorPaymentControlResu
         this(isAborted, null);
     }
 
+
+    public DefaultPriorPaymentControlResult(final PriorPaymentControlResult input, final Iterable<PluginProperty> adjustedPluginProperties) {
+        this(input.isAborted(), input.getAdjustedAmount(), input.getAdjustedCurrency(), input.getAdjustedPaymentMethodId(), adjustedPluginProperties);
+    }
+
     @Override
     public boolean isAborted() {
         return isAborted;