killbill-memoizeit
Changes
payment/src/main/java/org/killbill/billing/payment/control/InvoicePaymentControlPluginApi.java 9(+5 -4)
payment/src/main/java/org/killbill/billing/payment/core/PluginControlledPaymentProcessor.java 52(+34 -18)
payment/src/main/java/org/killbill/billing/payment/core/sm/PluginControlledPaymentAutomatonRunner.java 13(+7 -6)
payment/src/main/java/org/killbill/billing/payment/core/sm/RetryablePaymentStateContext.java 19(+10 -9)
payment/src/main/java/org/killbill/billing/payment/core/sm/RetryCompletionOperationCallback.java 2(+1 -1)
payment/src/main/java/org/killbill/billing/payment/provider/DefaultNoOpPaymentControlProviderPlugin.java 2(+1 -1)
payment/src/main/java/org/killbill/billing/payment/provider/DefaultPaymentControlProviderPlugin.java 2(+1 -1)
payment/src/main/java/org/killbill/billing/payment/retry/DefaultPriorPaymentControlResult.java 22(+20 -2)
payment/src/test/java/org/killbill/billing/payment/core/sm/MockRetryablePaymentAutomatonRunner.java 7(+5 -2)
Details
diff --git a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestIntegrationBase.java b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestIntegrationBase.java
index ada01fe..02c99b0 100644
--- a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestIntegrationBase.java
+++ b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestIntegrationBase.java
@@ -128,8 +128,8 @@ public class TestIntegrationBase extends BeatrixTestSuiteWithEmbeddedDB {
}
@Override
- public String getPaymentControlPluginName() {
- return InvoicePaymentControlPluginApi.PLUGIN_NAME;
+ public List<String> getPaymentControlPluginNames() {
+ return ImmutableList.<String>of(InvoicePaymentControlPluginApi.PLUGIN_NAME);
}
};
@@ -140,8 +140,8 @@ public class TestIntegrationBase extends BeatrixTestSuiteWithEmbeddedDB {
}
@Override
- public String getPaymentControlPluginName() {
- return InvoicePaymentControlPluginApi.PLUGIN_NAME;
+ public List<String> getPaymentControlPluginNames() {
+ return ImmutableList.<String>of(InvoicePaymentControlPluginApi.PLUGIN_NAME);
}
};
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/JaxRsResourceBase.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/JaxRsResourceBase.java
index e3d8de4..b95fa5b 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/JaxRsResourceBase.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/JaxRsResourceBase.java
@@ -368,9 +368,9 @@ public abstract class JaxRsResourceBase implements JaxrsResource {
}
@Override
- public String getPaymentControlPluginName() {
- /* Contract with plugin */
- return "__INVOICE_PAYMENT_CONTROL_PLUGIN__";
+ public List<String> getPaymentControlPluginNames() {
+ /* Will default to org.killbill.payment.control.plugin in payment sub-system */
+ return null;
}
};
}
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 3c0b151..15ec0cb 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
@@ -35,6 +35,7 @@ import org.killbill.billing.payment.core.PluginControlledPaymentProcessor;
import org.killbill.billing.util.callcontext.CallContext;
import org.killbill.billing.util.callcontext.InternalCallContextFactory;
import org.killbill.billing.util.callcontext.TenantContext;
+import org.killbill.billing.util.config.PaymentConfig;
import org.killbill.billing.util.entity.Pagination;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -47,13 +48,15 @@ public class DefaultPaymentApi implements PaymentApi {
private static final Logger log = LoggerFactory.getLogger(DefaultPaymentApi.class);
+ private final PaymentConfig paymentConfig;
private final PaymentProcessor paymentProcessor;
private final PaymentMethodProcessor paymentMethodProcessor;
private final PluginControlledPaymentProcessor pluginControlledPaymentProcessor;
private final InternalCallContextFactory internalCallContextFactory;
@Inject
- public DefaultPaymentApi(final PaymentProcessor paymentProcessor, final PaymentMethodProcessor paymentMethodProcessor, final PluginControlledPaymentProcessor pluginControlledPaymentProcessor, final InternalCallContextFactory internalCallContextFactory) {
+ public DefaultPaymentApi(final PaymentConfig paymentConfig, final PaymentProcessor paymentProcessor, final PaymentMethodProcessor paymentMethodProcessor, final PluginControlledPaymentProcessor pluginControlledPaymentProcessor, final InternalCallContextFactory internalCallContextFactory) {
+ this.paymentConfig = paymentConfig;
this.paymentProcessor = paymentProcessor;
this.paymentMethodProcessor = paymentMethodProcessor;
this.pluginControlledPaymentProcessor = pluginControlledPaymentProcessor;
@@ -137,7 +140,7 @@ public class DefaultPaymentApi implements PaymentApi {
paymentMethodId :
paymentMethodProcessor.createOrGetExternalPaymentMethod(UUID.randomUUID().toString(), account, properties, callContext, internalCallContext);
return pluginControlledPaymentProcessor.createPurchase(IS_API_PAYMENT, account, nonNulPaymentMethodId, paymentId, amount, currency, paymentExternalKey, paymentTransactionExternalKey,
- properties, paymentOptions.getPaymentControlPluginName(), callContext, internalCallContext);
+ properties, toPaymentControlPluginNames(paymentOptions), callContext, internalCallContext);
}
@@ -192,7 +195,7 @@ public class DefaultPaymentApi implements PaymentApi {
final InternalCallContext internalCallContext = internalCallContextFactory.createInternalCallContext(account.getId(), callContext);
return pluginControlledPaymentProcessor.createRefund(IS_API_PAYMENT, account, paymentId, amount, currency, paymentTransactionExternalKey,
- properties, paymentOptions.getPaymentControlPluginName(), callContext, internalCallContext);
+ properties, toPaymentControlPluginNames(paymentOptions), callContext, internalCallContext);
}
@@ -259,7 +262,7 @@ public class DefaultPaymentApi implements PaymentApi {
final InternalCallContext internalCallContext = internalCallContextFactory.createInternalCallContext(account.getId(), callContext);
return pluginControlledPaymentProcessor.createChargeback(account, paymentId, paymentTransactionExternalKey, amount, currency,
- paymentOptions.getPaymentControlPluginName(), callContext, internalCallContext);
+ toPaymentControlPluginNames(paymentOptions), callContext, internalCallContext);
}
@Override
@@ -423,6 +426,10 @@ public class DefaultPaymentApi implements PaymentApi {
}
}
+ private List<String> toPaymentControlPluginNames(final PaymentOptions paymentOptions) {
+ return paymentOptions.getPaymentControlPluginNames() != null ? paymentOptions.getPaymentControlPluginNames() : paymentConfig.getPaymentControlPluginNames();
+ }
+
private void checkNotNullParameter(final Object parameter, final String parameterName) throws PaymentApiException {
if (parameter == null) {
throw new PaymentApiException(ErrorCode.PAYMENT_INVALID_PARAMETER, parameterName, "should not be null");
diff --git a/payment/src/main/java/org/killbill/billing/payment/bus/InvoiceHandler.java b/payment/src/main/java/org/killbill/billing/payment/bus/InvoiceHandler.java
index a5fcee7..0157839 100644
--- a/payment/src/main/java/org/killbill/billing/payment/bus/InvoiceHandler.java
+++ b/payment/src/main/java/org/killbill/billing/payment/bus/InvoiceHandler.java
@@ -40,10 +40,12 @@ import org.killbill.billing.util.callcontext.CallContext;
import org.killbill.billing.util.callcontext.CallOrigin;
import org.killbill.billing.util.callcontext.InternalCallContextFactory;
import org.killbill.billing.util.callcontext.UserType;
+import org.killbill.billing.util.config.PaymentConfig;
import org.killbill.billing.util.dao.NonEntityDao;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import com.google.common.collect.ImmutableList;
import com.google.common.eventbus.Subscribe;
import com.google.inject.Inject;
@@ -54,15 +56,18 @@ public class InvoiceHandler {
private final PluginControlledPaymentProcessor pluginControlledPaymentProcessor;
private final NonEntityDao nonEntityDao;
private final CacheControllerDispatcher controllerDispatcher;
+ private final PaymentConfig paymentConfig;
private static final Logger log = LoggerFactory.getLogger(InvoiceHandler.class);
@Inject
- public InvoiceHandler(final AccountInternalApi accountApi,
+ public InvoiceHandler(final PaymentConfig paymentConfig,
+ final AccountInternalApi accountApi,
final PluginControlledPaymentProcessor pluginControlledPaymentProcessor,
final NonEntityDao nonEntityDao,
final InternalCallContextFactory internalCallContextFactory,
final CacheControllerDispatcher controllerDispatcher) {
+ this.paymentConfig = paymentConfig;
this.accountApi = accountApi;
this.internalCallContextFactory = internalCallContextFactory;
this.pluginControlledPaymentProcessor = pluginControlledPaymentProcessor;
@@ -88,8 +93,9 @@ public class InvoiceHandler {
final CallContext callContext = internalContext.toCallContext(nonEntityDao.retrieveIdFromObject(internalContext.getTenantRecordId(), ObjectType.TENANT, controllerDispatcher.getCacheController(CacheType.OBJECT_ID)));
final BigDecimal amountToBePaid = null; // We let the plugin compute how much should be paid
+ final List<String> paymentControlPluginNames = paymentConfig.getPaymentControlPluginNames() != null ? paymentConfig.getPaymentControlPluginNames() : ImmutableList.of(InvoicePaymentControlPluginApi.PLUGIN_NAME);
pluginControlledPaymentProcessor.createPurchase(false, account, account.getPaymentMethodId(), null, amountToBePaid, account.getCurrency(), UUID.randomUUID().toString(), UUID.randomUUID().toString(),
- properties, InvoicePaymentControlPluginApi.PLUGIN_NAME, callContext, internalContext);
+ properties, paymentControlPluginNames, callContext, internalContext);
} catch (final AccountApiException e) {
log.error("Failed to process invoice payment", e);
} catch (final PaymentApiException e) {
diff --git a/payment/src/main/java/org/killbill/billing/payment/control/InvoicePaymentControlPluginApi.java b/payment/src/main/java/org/killbill/billing/payment/control/InvoicePaymentControlPluginApi.java
index dbfe997..897f23c 100644
--- a/payment/src/main/java/org/killbill/billing/payment/control/InvoicePaymentControlPluginApi.java
+++ b/payment/src/main/java/org/killbill/billing/payment/control/InvoicePaymentControlPluginApi.java
@@ -124,7 +124,7 @@ public final class InvoicePaymentControlPluginApi implements PaymentControlPlugi
case REFUND:
return getPluginRefundResult(paymentControlContext, internalContext);
case CHARGEBACK:
- return new DefaultPriorPaymentControlResult(false, paymentControlContext.getAmount());
+ return new DefaultPriorPaymentControlResult(false, paymentControlContext.getAmount(), null, null);
default:
throw new IllegalStateException("Unexpected transactionType " + transactionType);
}
@@ -209,7 +209,8 @@ public final class InvoicePaymentControlPluginApi implements PaymentControlPlugi
public void process_AUTO_PAY_OFF_removal(final Account account, final InternalCallContext internalCallContext) {
final List<PluginAutoPayOffModelDao> entries = controlDao.getAutoPayOffEntry(account.getId());
for (PluginAutoPayOffModelDao cur : entries) {
- retryServiceScheduler.scheduleRetry(ObjectType.ACCOUNT, account.getId(), cur.getAttemptId(), PLUGIN_NAME, clock.getUTCNow());
+ // TODO In theory we should pass not only PLUGIN_NAME, but also all the plugin list associated which the original call
+ retryServiceScheduler.scheduleRetry(ObjectType.ACCOUNT, account.getId(), cur.getAttemptId(), ImmutableList.<String>of(PLUGIN_NAME), clock.getUTCNow());
}
controlDao.removeAutoPayOffEntry(account.getId());
}
@@ -240,7 +241,7 @@ public final class InvoicePaymentControlPluginApi implements PaymentControlPlugi
" aborted : invoice balance is = " + invoice.getBalance() +
", requested payment amount is = " + paymentControlPluginContext.getAmount());
} else {
- return new DefaultPriorPaymentControlResult(isAborted, requestedAmount);
+ return new DefaultPriorPaymentControlResult(isAborted, requestedAmount, null, null);
}
} catch (InvoiceApiException e) {
throw new PaymentControlApiException(e);
@@ -273,7 +274,7 @@ public final class InvoicePaymentControlPluginApi implements PaymentControlPlugi
" aborted : invoice item sum amount is " + amountToBeRefunded +
", requested refund amount is = " + paymentControlPluginContext.getAmount());
} else {
- return new DefaultPriorPaymentControlResult(isAborted, amountToBeRefunded);
+ return new DefaultPriorPaymentControlResult(isAborted, amountToBeRefunded, null, null);
}
}
diff --git a/payment/src/main/java/org/killbill/billing/payment/core/janitor/AttemptCompletionTask.java b/payment/src/main/java/org/killbill/billing/payment/core/janitor/AttemptCompletionTask.java
index 5714907..717142d 100644
--- a/payment/src/main/java/org/killbill/billing/payment/core/janitor/AttemptCompletionTask.java
+++ b/payment/src/main/java/org/killbill/billing/payment/core/janitor/AttemptCompletionTask.java
@@ -96,7 +96,7 @@ final class AttemptCompletionTask extends CompletionTaskBase<PaymentAttemptModel
final Account account = accountInternalApi.getAccountById(attempt.getAccountId(), tenantContext);
final boolean isApiPayment = true; // unclear
- final RetryablePaymentStateContext paymentStateContext = new RetryablePaymentStateContext(attempt.getPluginName(),
+ final RetryablePaymentStateContext paymentStateContext = new RetryablePaymentStateContext(attempt.toPaymentControlPluginNames(),
isApiPayment,
transaction.getPaymentId(),
attempt.getPaymentExternalKey(),
diff --git a/payment/src/main/java/org/killbill/billing/payment/core/PluginControlledPaymentProcessor.java b/payment/src/main/java/org/killbill/billing/payment/core/PluginControlledPaymentProcessor.java
index 9f5fbda..c6c5979 100644
--- a/payment/src/main/java/org/killbill/billing/payment/core/PluginControlledPaymentProcessor.java
+++ b/payment/src/main/java/org/killbill/billing/payment/core/PluginControlledPaymentProcessor.java
@@ -17,11 +17,13 @@
package org.killbill.billing.payment.core;
import java.math.BigDecimal;
+import java.util.List;
import java.util.UUID;
import java.util.concurrent.ExecutorService;
import javax.annotation.Nullable;
import javax.inject.Inject;
+import javax.sql.rowset.Joinable;
import org.killbill.automaton.MissingEntryException;
import org.killbill.automaton.State;
@@ -53,6 +55,7 @@ import org.killbill.billing.util.dao.NonEntityDao;
import org.killbill.clock.Clock;
import org.killbill.commons.locker.GlobalLocker;
+import com.google.common.base.Joiner;
import com.google.common.collect.ImmutableList;
import com.google.inject.name.Named;
@@ -60,6 +63,8 @@ import static org.killbill.billing.payment.glue.PaymentModule.PLUGIN_EXECUTOR_NA
public class PluginControlledPaymentProcessor extends ProcessorBase {
+ private static final Joiner JOINER = Joiner.on(", ");
+
private final PluginControlledPaymentAutomatonRunner pluginControlledPaymentAutomatonRunner;
private final RetryStateMachineHelper retrySMHelper;
private final CacheControllerDispatcher controllerDispatcher;
@@ -84,7 +89,7 @@ public class PluginControlledPaymentProcessor extends ProcessorBase {
}
public Payment createAuthorization(final boolean isApiPayment, final Account account, final UUID paymentMethodId, @Nullable final UUID paymentId, final BigDecimal amount, final Currency currency, final String paymentExternalKey, final String transactionExternalKey,
- final Iterable<PluginProperty> properties, final String paymentControlPluginName, final CallContext callContext, final InternalCallContext internalCallContext) throws PaymentApiException {
+ final Iterable<PluginProperty> properties, final List<String> paymentControlPluginNames, final CallContext callContext, final InternalCallContext internalCallContext) throws PaymentApiException {
return pluginControlledPaymentAutomatonRunner.run(isApiPayment,
TransactionType.AUTHORIZE,
account,
@@ -95,13 +100,13 @@ public class PluginControlledPaymentProcessor extends ProcessorBase {
amount,
currency,
properties,
- paymentControlPluginName,
+ paymentControlPluginNames,
callContext, internalCallContext);
}
public Payment createCapture(final boolean isApiPayment, final Account account, final UUID paymentId, final BigDecimal amount, final Currency currency,
final String transactionExternalKey,
- final Iterable<PluginProperty> properties, final String paymentControlPluginName,
+ final Iterable<PluginProperty> properties, final List<String> paymentControlPluginNames,
final CallContext callContext, final InternalCallContext internalCallContext) throws PaymentApiException {
return pluginControlledPaymentAutomatonRunner.run(isApiPayment,
TransactionType.CAPTURE,
@@ -113,13 +118,13 @@ public class PluginControlledPaymentProcessor extends ProcessorBase {
amount,
currency,
properties,
- paymentControlPluginName,
+ paymentControlPluginNames,
callContext, internalCallContext);
}
public Payment createPurchase(final boolean isApiPayment, final Account account, final UUID paymentMethodId, final UUID paymentId, final BigDecimal amount, final Currency currency,
final String paymentExternalKey, final String transactionExternalKey, final Iterable<PluginProperty> properties,
- final String paymentControlPluginName, final CallContext callContext, final InternalCallContext internalCallContext) throws PaymentApiException {
+ final List<String> paymentControlPluginNames, final CallContext callContext, final InternalCallContext internalCallContext) throws PaymentApiException {
return pluginControlledPaymentAutomatonRunner.run(isApiPayment,
TransactionType.PURCHASE,
account,
@@ -130,7 +135,7 @@ public class PluginControlledPaymentProcessor extends ProcessorBase {
amount,
currency,
properties,
- paymentControlPluginName,
+ paymentControlPluginNames,
callContext, internalCallContext);
}
@@ -151,7 +156,7 @@ public class PluginControlledPaymentProcessor extends ProcessorBase {
}
public Payment createRefund(final boolean isApiPayment, final Account account, final UUID paymentId, final BigDecimal amount, final Currency currency, final String transactionExternalKey,
- final Iterable<PluginProperty> properties, final String paymentControlPluginName, final CallContext callContext, final InternalCallContext internalCallContext) throws PaymentApiException {
+ final Iterable<PluginProperty> properties, final List<String> paymentControlPluginNames, final CallContext callContext, final InternalCallContext internalCallContext) throws PaymentApiException {
return pluginControlledPaymentAutomatonRunner.run(isApiPayment,
TransactionType.REFUND,
account,
@@ -162,12 +167,12 @@ public class PluginControlledPaymentProcessor extends ProcessorBase {
amount,
currency,
properties,
- paymentControlPluginName,
+ paymentControlPluginNames,
callContext, internalCallContext);
}
public Payment createCredit(final boolean isApiPayment, final Account account, final UUID paymentMethodId, final UUID paymentId, final BigDecimal amount, final Currency currency, final String paymentExternalKey,
- final String transactionExternalKey, final Iterable<PluginProperty> properties, final String paymentControlPluginName, final CallContext callContext, final InternalCallContext internalCallContext) throws PaymentApiException {
+ final String transactionExternalKey, final Iterable<PluginProperty> properties, final List<String> paymentControlPluginNames, final CallContext callContext, final InternalCallContext internalCallContext) throws PaymentApiException {
return pluginControlledPaymentAutomatonRunner.run(isApiPayment,
TransactionType.CREDIT,
@@ -179,12 +184,12 @@ public class PluginControlledPaymentProcessor extends ProcessorBase {
amount,
currency,
properties,
- paymentControlPluginName,
+ paymentControlPluginNames,
callContext, internalCallContext);
}
public Payment createChargeback(final Account account, final UUID paymentId, final String transactionExternalKey, final BigDecimal amount, final Currency currency,
- final String paymentControlPluginName, final CallContext callContext, final InternalCallContext internalCallContext) throws PaymentApiException {
+ final List<String> paymentControlPluginNames, final CallContext callContext, final InternalCallContext internalCallContext) throws PaymentApiException {
return pluginControlledPaymentAutomatonRunner.run(true,
TransactionType.CHARGEBACK,
account,
@@ -195,11 +200,11 @@ public class PluginControlledPaymentProcessor extends ProcessorBase {
amount,
currency,
ImmutableList.<PluginProperty>of(),
- paymentControlPluginName,
+ paymentControlPluginNames,
callContext, internalCallContext);
}
- public void retryPaymentTransaction(final UUID attemptId, final String pluginName, final InternalCallContext internalCallContext) {
+ public void retryPaymentTransaction(final UUID attemptId, final List<String> paymentControlPluginNames, final InternalCallContext internalCallContext) {
try {
final PaymentAttemptModelDao attempt = paymentDao.getPaymentAttempt(attemptId, internalCallContext);
@@ -223,18 +228,29 @@ public class PluginControlledPaymentProcessor extends ProcessorBase {
attempt.getAmount(),
attempt.getCurrency(),
pluginProperties,
- pluginName,
+ paymentControlPluginNames,
callContext,
internalCallContext);
} catch (AccountApiException e) {
- log.warn("Failed to retry attempt " + attemptId + " for plugin " + pluginName, e);
+ log.warn("Failed to retry attempt " + attemptId + toPluginNamesOnError(" for plugins ", paymentControlPluginNames), e);
} catch (PaymentApiException e) {
- log.warn("Failed to retry attempt " + attemptId + " for plugin " + pluginName, e);
+ log.warn("Failed to retry attempt " + attemptId + toPluginNamesOnError(" for plugins ", paymentControlPluginNames), e);
} catch (PluginPropertySerializerException e) {
- log.warn("Failed to retry attempt " + attemptId + " for plugin " + pluginName, e);
+ log.warn("Failed to retry attempt " + attemptId + toPluginNamesOnError(" for plugins ", paymentControlPluginNames), e);
} catch (MissingEntryException e) {
- log.warn("Failed to retry attempt " + attemptId + " for plugin " + pluginName, e);
+ log.warn("Failed to retry attempt " + attemptId + toPluginNamesOnError(" for plugins ", paymentControlPluginNames), e);
+ }
+ }
+
+ private String toPluginNamesOnError(final String prefixMessage, final List<String> paymentControlPluginNames) {
+ if (paymentControlPluginNames == null || paymentControlPluginNames.size() == 0) {
+ return "";
}
+ final StringBuilder tmp = new StringBuilder(prefixMessage)
+ .append("(")
+ .append(JOINER.join(paymentControlPluginNames))
+ .append(")");
+ return tmp.toString();
}
}
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 32b18a2..3aafe01 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
@@ -47,13 +47,13 @@ public class PaymentStateContext {
protected PaymentTransactionInfoPlugin paymentInfoPlugin;
protected BigDecimal amount;
protected String paymentExternalKey;
+ protected Currency currency;
// Can be updated later via paymentTransactionModelDao (e.g. for auth or purchase)
protected final UUID paymentId;
protected final UUID transactionId;
protected final String paymentTransactionExternalKey;
protected final Account account;
- protected final Currency currency;
protected final TransactionType transactionType;
protected final boolean shouldLockAccountAndDispatch;
protected final Iterable<PluginProperty> properties;
diff --git a/payment/src/main/java/org/killbill/billing/payment/core/sm/PluginControlledPaymentAutomatonRunner.java b/payment/src/main/java/org/killbill/billing/payment/core/sm/PluginControlledPaymentAutomatonRunner.java
index ec4280c..0df1324 100644
--- a/payment/src/main/java/org/killbill/billing/payment/core/sm/PluginControlledPaymentAutomatonRunner.java
+++ b/payment/src/main/java/org/killbill/billing/payment/core/sm/PluginControlledPaymentAutomatonRunner.java
@@ -17,6 +17,7 @@
package org.killbill.billing.payment.core.sm;
import java.math.BigDecimal;
+import java.util.List;
import java.util.UUID;
import java.util.concurrent.ExecutorService;
@@ -81,23 +82,23 @@ public class PluginControlledPaymentAutomatonRunner extends PaymentAutomatonRunn
public Payment run(final boolean isApiPayment, final TransactionType transactionType, final Account account, @Nullable final UUID paymentMethodId,
@Nullable final UUID paymentId, @Nullable final String paymentExternalKey, final String paymentTransactionExternalKey,
@Nullable final BigDecimal amount, @Nullable final Currency currency,
- final Iterable<PluginProperty> properties, @Nullable final String pluginName,
+ final Iterable<PluginProperty> properties, @Nullable final List<String> paymentControlPluginNames,
final CallContext callContext, final InternalCallContext internalCallContext) throws PaymentApiException {
return run(retrySMHelper.getInitialState(), isApiPayment, transactionType, account, paymentMethodId, paymentId, paymentExternalKey, paymentTransactionExternalKey,
- amount, currency, properties, pluginName, callContext, internalCallContext);
+ amount, currency, properties, paymentControlPluginNames, callContext, internalCallContext);
}
public Payment run(final State state, final boolean isApiPayment, final TransactionType transactionType, final Account account, @Nullable final UUID paymentMethodId,
@Nullable final UUID paymentId, @Nullable final String paymentExternalKey, final String paymentTransactionExternalKey,
@Nullable final BigDecimal amount, @Nullable final Currency currency,
- final Iterable<PluginProperty> properties, @Nullable final String pluginName,
+ final Iterable<PluginProperty> properties, @Nullable final List<String> paymentControlPluginNames,
final CallContext callContext, final InternalCallContext internalCallContext) throws PaymentApiException {
final RetryablePaymentStateContext paymentStateContext = createContext(isApiPayment, transactionType, account, paymentMethodId,
paymentId, paymentExternalKey,
paymentTransactionExternalKey,
amount, currency,
- properties, pluginName, callContext, internalCallContext);
+ properties, paymentControlPluginNames, callContext, internalCallContext);
try {
final OperationCallback callback = createOperationCallback(transactionType, paymentStateContext);
@@ -149,8 +150,8 @@ public class PluginControlledPaymentAutomatonRunner extends PaymentAutomatonRunn
RetryablePaymentStateContext createContext(final boolean isApiPayment, final TransactionType transactionType, final Account account, @Nullable final UUID paymentMethodId,
@Nullable final UUID paymentId, @Nullable final String paymentExternalKey, final String paymentTransactionExternalKey,
@Nullable final BigDecimal amount, @Nullable final Currency currency, final Iterable<PluginProperty> properties,
- final String pluginName, final CallContext callContext, final InternalCallContext internalCallContext) throws PaymentApiException {
- return new RetryablePaymentStateContext(pluginName, isApiPayment, paymentId, paymentExternalKey, paymentTransactionExternalKey, transactionType, account,
+ final List<String> paymentControlPluginNames, final CallContext callContext, final InternalCallContext internalCallContext) throws PaymentApiException {
+ return new RetryablePaymentStateContext(paymentControlPluginNames, isApiPayment, paymentId, paymentExternalKey, paymentTransactionExternalKey, transactionType, account,
paymentMethodId, amount, currency, properties, internalCallContext, callContext);
}
diff --git a/payment/src/main/java/org/killbill/billing/payment/core/sm/RetryablePaymentStateContext.java b/payment/src/main/java/org/killbill/billing/payment/core/sm/RetryablePaymentStateContext.java
index 678d987..7461aca 100644
--- a/payment/src/main/java/org/killbill/billing/payment/core/sm/RetryablePaymentStateContext.java
+++ b/payment/src/main/java/org/killbill/billing/payment/core/sm/RetryablePaymentStateContext.java
@@ -17,6 +17,7 @@
package org.killbill.billing.payment.core.sm;
import java.math.BigDecimal;
+import java.util.List;
import java.util.UUID;
import javax.annotation.Nullable;
@@ -38,15 +39,15 @@ import com.google.common.collect.Iterables;
public class RetryablePaymentStateContext extends PaymentStateContext {
private DateTime retryDate;
- private String pluginName;
+ private List<String> paymentControlPluginNames;
private Payment result;
- public RetryablePaymentStateContext(@Nullable final String pluginName, final boolean isApiPayment, @Nullable final UUID paymentId, final String paymentExternalKey,
+ public RetryablePaymentStateContext(@Nullable final List<String> paymentControlPluginNames, final boolean isApiPayment, @Nullable final UUID paymentId, final String paymentExternalKey,
@Nullable final String paymentTransactionExternalKey, final TransactionType transactionType,
final Account account, @Nullable final UUID paymentMethodId, final BigDecimal amount, final Currency currency,
final Iterable<PluginProperty> properties, final InternalCallContext internalCallContext, final CallContext callContext) {
super(isApiPayment, paymentId, null, null, paymentExternalKey, paymentTransactionExternalKey, transactionType, account, paymentMethodId, amount, currency, true, null, properties, internalCallContext, callContext);
- this.pluginName = pluginName;
+ this.paymentControlPluginNames = paymentControlPluginNames;
}
public DateTime getRetryDate() {
@@ -57,12 +58,8 @@ public class RetryablePaymentStateContext extends PaymentStateContext {
this.retryDate = retryDate;
}
- public String getPluginName() {
- return pluginName;
- }
-
- public void setPluginName(final String pluginName) {
- this.pluginName = pluginName;
+ public List<String> getPaymentControlPluginNames() {
+ return paymentControlPluginNames;
}
public Payment getResult() {
@@ -77,6 +74,10 @@ public class RetryablePaymentStateContext extends PaymentStateContext {
this.amount = adjustedAmount;
}
+ public void setCurrency(final Currency currency) {
+ this.currency = currency;
+ }
+
public PaymentTransaction getCurrentTransaction() {
if (result == null || result.getTransactions() == null) {
return null;
diff --git a/payment/src/main/java/org/killbill/billing/payment/core/sm/RetryCompletionOperationCallback.java b/payment/src/main/java/org/killbill/billing/payment/core/sm/RetryCompletionOperationCallback.java
index 91e1f92..4dab872 100644
--- a/payment/src/main/java/org/killbill/billing/payment/core/sm/RetryCompletionOperationCallback.java
+++ b/payment/src/main/java/org/killbill/billing/payment/core/sm/RetryCompletionOperationCallback.java
@@ -60,7 +60,7 @@ public class RetryCompletionOperationCallback extends RetryOperationCallback {
retryablePaymentStateContext.isApiPayment(),
paymentStateContext.callContext);
- onCompletion(retryablePaymentStateContext.getPluginName(), updatedPaymentControlContext);
+ onCompletion(retryablePaymentStateContext.getPaymentControlPluginNames(), updatedPaymentControlContext);
return PluginDispatcher.createPluginDispatcherReturnType(OperationResult.SUCCESS);
}
});
diff --git a/payment/src/main/java/org/killbill/billing/payment/core/sm/RetryEnteringStateCallback.java b/payment/src/main/java/org/killbill/billing/payment/core/sm/RetryEnteringStateCallback.java
index 0b7889f..1b9554d 100644
--- a/payment/src/main/java/org/killbill/billing/payment/core/sm/RetryEnteringStateCallback.java
+++ b/payment/src/main/java/org/killbill/billing/payment/core/sm/RetryEnteringStateCallback.java
@@ -51,7 +51,7 @@ public class RetryEnteringStateCallback implements EnteringStateCallback {
if ("RETRIED".equals(state.getName())) {
retryServiceScheduler.scheduleRetry(ObjectType.PAYMENT_ATTEMPT, attempt.getId(), attempt.getId(),
- paymentStateContext.getPluginName(), paymentStateContext.getRetryDate());
+ paymentStateContext.getPaymentControlPluginNames(), paymentStateContext.getRetryDate());
}
}
}
diff --git a/payment/src/main/java/org/killbill/billing/payment/core/sm/RetryLeavingStateCallback.java b/payment/src/main/java/org/killbill/billing/payment/core/sm/RetryLeavingStateCallback.java
index 4a4b2ed..cd9f2c3 100644
--- a/payment/src/main/java/org/killbill/billing/payment/core/sm/RetryLeavingStateCallback.java
+++ b/payment/src/main/java/org/killbill/billing/payment/core/sm/RetryLeavingStateCallback.java
@@ -73,7 +73,7 @@ public class RetryLeavingStateCallback implements LeavingStateCallback {
utcNow, utcNow, stateContext.getPaymentExternalKey(), null,
stateContext.paymentTransactionExternalKey, transactionType, initialState.getName(),
stateContext.getAmount(), stateContext.getCurrency(),
- stateContext.getPluginName(), serializedProperties);
+ stateContext.getPaymentControlPluginNames(), serializedProperties);
retryablePaymentAutomatonRunner.paymentDao.insertPaymentAttemptWithProperties(attempt, stateContext.internalCallContext);
stateContext.setAttemptId(attempt.getId());
diff --git a/payment/src/main/java/org/killbill/billing/payment/core/sm/RetryOperationCallback.java b/payment/src/main/java/org/killbill/billing/payment/core/sm/RetryOperationCallback.java
index 580e180..7d9acdf 100644
--- a/payment/src/main/java/org/killbill/billing/payment/core/sm/RetryOperationCallback.java
+++ b/payment/src/main/java/org/killbill/billing/payment/core/sm/RetryOperationCallback.java
@@ -17,6 +17,7 @@
package org.killbill.billing.payment.core.sm;
import java.math.BigDecimal;
+import java.util.List;
import java.util.UUID;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeoutException;
@@ -94,7 +95,7 @@ public abstract class RetryOperationCallback extends OperationCallbackBase<Payme
final PriorPaymentControlResult pluginResult;
try {
- pluginResult = getPluginResult(retryablePaymentStateContext.getPluginName(), paymentControlContext);
+ pluginResult = getPluginResult(retryablePaymentStateContext.getPaymentControlPluginNames(), paymentControlContext);
if (pluginResult.isAborted()) {
// Transition to ABORTED
return PluginDispatcher.createPluginDispatcherReturnType(OperationResult.EXCEPTION);
@@ -107,10 +108,7 @@ public abstract class RetryOperationCallback extends OperationCallbackBase<Payme
final boolean success;
try {
// Adjust amount with value returned by plugin if necessary
- if (paymentStateContext.getAmount() == null ||
- (pluginResult.getAdjustedAmount() != null && pluginResult.getAdjustedAmount().compareTo(paymentStateContext.getAmount()) != 0)) {
- ((RetryablePaymentStateContext) paymentStateContext).setAmount(pluginResult.getAdjustedAmount());
- }
+ adjustStateContextValues(paymentStateContext, pluginResult);
final Payment result = doCallSpecificOperationCallback();
((RetryablePaymentStateContext) paymentStateContext).setResult(result);
@@ -134,7 +132,7 @@ public abstract class RetryOperationCallback extends OperationCallbackBase<Payme
retryablePaymentStateContext.isApiPayment(),
paymentStateContext.callContext);
- onCompletion(retryablePaymentStateContext.getPluginName(), updatedPaymentControlContext);
+ onCompletion(retryablePaymentStateContext.getPaymentControlPluginNames(), updatedPaymentControlContext);
return PluginDispatcher.createPluginDispatcherReturnType(OperationResult.SUCCESS);
} else {
throw new OperationException(null, getOperationResultAndSetContext(retryablePaymentStateContext, paymentControlContext));
@@ -177,12 +175,30 @@ public abstract class RetryOperationCallback extends OperationCallbackBase<Payme
return new OperationException(e, getOperationResultOnException(paymentStateContext));
}
- protected void onCompletion(final String pluginName, final PaymentControlContext paymentControlContext) {
- final PaymentControlPluginApi plugin = paymentControlPluginRegistry.getServiceForName(pluginName);
- try {
- plugin.onSuccessCall(paymentControlContext);
- } catch (final PaymentControlApiException e) {
- logger.warn("Plugin " + pluginName + " failed to complete onCompletion call for " + paymentControlContext.getPaymentExternalKey(), e);
+ protected void onCompletion(final List<String> paymentControlPluginNames, final PaymentControlContext paymentControlContext) {
+ for (String pluginName : paymentControlPluginNames) {
+ final PaymentControlPluginApi plugin = paymentControlPluginRegistry.getServiceForName(pluginName);
+ if (plugin != null) {
+ try {
+ plugin.onSuccessCall(paymentControlContext);
+ } catch (final PaymentControlApiException e) {
+ logger.warn("Plugin " + pluginName + " failed to complete onCompletion call for " + paymentControlContext.getPaymentExternalKey(), e);
+ }
+ }
+ }
+ }
+
+ private final void adjustStateContextValues(final PaymentStateContext inputContext, final PriorPaymentControlResult pluginResult) {
+
+ final RetryablePaymentStateContext input = (RetryablePaymentStateContext) 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());
}
}
@@ -192,14 +208,43 @@ public abstract class RetryOperationCallback extends OperationCallbackBase<Payme
return operationResult;
}
- private PriorPaymentControlResult getPluginResult(final String pluginName, final PaymentControlContext paymentControlContext) throws PaymentControlApiException {
- final PaymentControlPluginApi plugin = paymentControlPluginRegistry.getServiceForName(pluginName);
- final PriorPaymentControlResult result = plugin.priorCall(paymentControlContext);
- return result;
+ private PriorPaymentControlResult getPluginResult(final List<String> paymentControlPluginNames, final PaymentControlContext paymentControlContextArg) throws PaymentControlApiException {
+
+ // Return as soon as the first plugin aborts, or the last result for the last plugin
+ PriorPaymentControlResult prevResult = null;
+
+ PaymentControlContext inputPaymentControlContext = paymentControlContextArg;
+
+ for (String pluginName : paymentControlPluginNames) {
+ final PaymentControlPluginApi plugin = paymentControlPluginRegistry.getServiceForName(pluginName);
+ if (plugin == null) {
+ // First call to plugin, we log warn, if plugin is not registered
+ logger.warn("Skipping payment plugin control {} when fetching results", pluginName);
+ continue;
+ }
+ prevResult = plugin.priorCall(inputPaymentControlContext);
+ if (prevResult.isAborted()) {
+ break;
+ }
+ inputPaymentControlContext = new DefaultPaymentControlContext(paymentStateContext.getAccount(),
+ prevResult.getAdjustedPaymentMethodId() != null ? prevResult.getAdjustedPaymentMethodId() : inputPaymentControlContext.getPaymentMethodId(),
+ retryablePaymentStateContext.getAttemptId(),
+ paymentStateContext.getPaymentId(),
+ paymentStateContext.getPaymentExternalKey(),
+ paymentStateContext.getPaymentTransactionExternalKey(),
+ paymentStateContext.getTransactionType(),
+ prevResult.getAdjustedAmount() != null ? prevResult.getAdjustedAmount() : inputPaymentControlContext.getAmount(),
+ prevResult.getAdjustedCurrency() != null ? prevResult.getAdjustedCurrency() : inputPaymentControlContext.getCurrency(),
+ paymentStateContext.getProperties(),
+ retryablePaymentStateContext.isApiPayment(),
+ paymentStateContext.callContext);
+
+ }
+ return prevResult;
}
private OperationResult getOperationResultAndSetContext(final RetryablePaymentStateContext retryablePaymentStateContext, final PaymentControlContext paymentControlContext) {
- final DateTime retryDate = getNextRetryDate(retryablePaymentStateContext.getPluginName(), paymentControlContext);
+ final DateTime retryDate = getNextRetryDate(retryablePaymentStateContext.getPaymentControlPluginNames(), paymentControlContext);
if (retryDate != null) {
((RetryablePaymentStateContext) paymentStateContext).setRetryDate(retryDate);
return OperationResult.FAILURE;
@@ -208,15 +253,25 @@ public abstract class RetryOperationCallback extends OperationCallbackBase<Payme
}
}
- private DateTime getNextRetryDate(final String pluginName, final PaymentControlContext paymentControlContext) {
- try {
+ private DateTime getNextRetryDate(final List<String> paymentControlPluginNames, final PaymentControlContext paymentControlContext) {
+ DateTime candidate = null;
+ for (String pluginName : paymentControlPluginNames) {
final PaymentControlPluginApi plugin = paymentControlPluginRegistry.getServiceForName(pluginName);
- final FailureCallResult result = plugin.onFailureCall(paymentControlContext);
- return result.getNextRetryDate();
- } catch (final PaymentControlApiException e) {
- logger.warn("Plugin " + pluginName + " failed to return next retryDate for payment " + paymentControlContext.getPaymentExternalKey(), e);
- return null;
+ if (plugin != null) {
+ try {
+ final FailureCallResult result = plugin.onFailureCall(paymentControlContext);
+ if (candidate == null) {
+ candidate = result.getNextRetryDate();
+ } else if (result.getNextRetryDate() != null) {
+ candidate = candidate.compareTo(result.getNextRetryDate()) > 0 ? result.getNextRetryDate() : candidate;
+ }
+ } catch (final PaymentControlApiException e) {
+ logger.warn("Plugin " + pluginName + " failed to return next retryDate for payment " + paymentControlContext.getPaymentExternalKey(), e);
+ return candidate;
+ }
+ }
}
+ return candidate;
}
public static class DefaultPaymentControlContext extends DefaultCallContext implements PaymentControlContext {
diff --git a/payment/src/main/java/org/killbill/billing/payment/dao/PaymentAttemptModelDao.java b/payment/src/main/java/org/killbill/billing/payment/dao/PaymentAttemptModelDao.java
index fdc1354..85d218a 100644
--- a/payment/src/main/java/org/killbill/billing/payment/dao/PaymentAttemptModelDao.java
+++ b/payment/src/main/java/org/killbill/billing/payment/dao/PaymentAttemptModelDao.java
@@ -31,8 +31,13 @@ import org.killbill.billing.util.entity.Entity;
import org.killbill.billing.util.entity.dao.EntityModelDao;
import org.killbill.billing.util.entity.dao.EntityModelDaoBase;
+import com.google.common.base.Joiner;
+import com.google.common.collect.ImmutableList;
+
public class PaymentAttemptModelDao extends EntityModelDaoBase implements EntityModelDao<Entity> {
+ private static final Joiner JOINER = Joiner.on(",");
+
private UUID accountId;
private UUID paymentMethodId;
private String paymentExternalKey;
@@ -66,9 +71,9 @@ public class PaymentAttemptModelDao extends EntityModelDaoBase implements Entity
public PaymentAttemptModelDao(final UUID accountId, final UUID paymentMethodId, @Nullable final DateTime createdDate, @Nullable final DateTime updatedDate,
final String paymentExternalKey, final UUID transactionId, final String transactionExternalKey, final TransactionType transactionType, final String stateName,
- final BigDecimal amount, final Currency currency, final String pluginName, final byte [] pluginProperties) {
+ final BigDecimal amount, final Currency currency, final List<String> paymentControlPluginNames, final byte [] pluginProperties) {
this(accountId, paymentMethodId, UUID.randomUUID(), createdDate, updatedDate, paymentExternalKey, transactionId, transactionExternalKey, transactionType, stateName,
- amount, currency, pluginName, pluginProperties);
+ amount, currency, toPluginNames(paymentControlPluginNames), pluginProperties);
}
public String getPaymentExternalKey() {
@@ -159,6 +164,14 @@ public class PaymentAttemptModelDao extends EntityModelDaoBase implements Entity
this.currency = currency;
}
+ public final List<String> toPaymentControlPluginNames() {
+ if (pluginName == null) {
+ return ImmutableList.<String>of();
+ }
+ final String [] parts = pluginName.split(",");
+ return ImmutableList.<String>copyOf(parts);
+ }
+
@Override
public boolean equals(final Object o) {
if (this == o) {
@@ -233,4 +246,7 @@ public class PaymentAttemptModelDao extends EntityModelDaoBase implements Entity
return TableName.PAYMENT_ATTEMPT_HISTORY;
}
+ private static final String toPluginNames(final List<String> paymentControlPluginNames) {
+ return paymentControlPluginNames == null || paymentControlPluginNames.size() == 0 ? null : JOINER.join(paymentControlPluginNames);
+ }
}
diff --git a/payment/src/main/java/org/killbill/billing/payment/provider/DefaultNoOpPaymentControlProviderPlugin.java b/payment/src/main/java/org/killbill/billing/payment/provider/DefaultNoOpPaymentControlProviderPlugin.java
index 33ddf53..e6a2fc4 100644
--- a/payment/src/main/java/org/killbill/billing/payment/provider/DefaultNoOpPaymentControlProviderPlugin.java
+++ b/payment/src/main/java/org/killbill/billing/payment/provider/DefaultNoOpPaymentControlProviderPlugin.java
@@ -33,7 +33,7 @@ public class DefaultNoOpPaymentControlProviderPlugin implements PaymentControlPl
@Override
public PriorPaymentControlResult priorCall(final PaymentControlContext retryPluginContext) throws PaymentControlApiException {
- return new DefaultPriorPaymentControlResult(isRetryAborted, null);
+ return new DefaultPriorPaymentControlResult(isRetryAborted, null, null, null);
}
@Override
diff --git a/payment/src/main/java/org/killbill/billing/payment/provider/DefaultPaymentControlProviderPlugin.java b/payment/src/main/java/org/killbill/billing/payment/provider/DefaultPaymentControlProviderPlugin.java
index 575c1e5..2d82d9f 100644
--- a/payment/src/main/java/org/killbill/billing/payment/provider/DefaultPaymentControlProviderPlugin.java
+++ b/payment/src/main/java/org/killbill/billing/payment/provider/DefaultPaymentControlProviderPlugin.java
@@ -30,7 +30,7 @@ public class DefaultPaymentControlProviderPlugin implements PaymentControlPlugin
@Override
public PriorPaymentControlResult priorCall(final PaymentControlContext paymentControlContext) throws PaymentControlApiException {
- return new DefaultPriorPaymentControlResult(false, null);
+ return new DefaultPriorPaymentControlResult(false, null, null, null);
}
@Override
diff --git a/payment/src/main/java/org/killbill/billing/payment/retry/BaseRetryService.java b/payment/src/main/java/org/killbill/billing/payment/retry/BaseRetryService.java
index b72d4c7..659d344 100644
--- a/payment/src/main/java/org/killbill/billing/payment/retry/BaseRetryService.java
+++ b/payment/src/main/java/org/killbill/billing/payment/retry/BaseRetryService.java
@@ -19,6 +19,7 @@
package org.killbill.billing.payment.retry;
import java.io.IOException;
+import java.util.List;
import java.util.UUID;
import org.joda.time.DateTime;
@@ -70,7 +71,7 @@ public abstract class BaseRetryService implements RetryService {
}
final PaymentRetryNotificationKey key = (PaymentRetryNotificationKey) notificationKey;
final InternalCallContext callContext = internalCallContextFactory.createInternalCallContext(tenantRecordId, accountRecordId, PAYMENT_RETRY_SERVICE, CallOrigin.INTERNAL, UserType.SYSTEM, userToken);
- retryPaymentTransaction(key.getAttemptId(), key.getPluginName(), callContext);
+ retryPaymentTransaction(key.getAttemptId(), key.getPaymentControlPluginNames(), callContext);
}
}
);
@@ -104,17 +105,17 @@ public abstract class BaseRetryService implements RetryService {
this.internalCallContextFactory = internalCallContextFactory;
}
- public boolean scheduleRetry(final ObjectType objectType, final UUID objectId, final UUID attemptId, final String pluginName, final DateTime timeOfRetry) {
- return scheduleRetryInternal(objectType, objectId, attemptId, pluginName, timeOfRetry, null);
+ public boolean scheduleRetry(final ObjectType objectType, final UUID objectId, final UUID attemptId, final List<String> paymentControlPluginNames, final DateTime timeOfRetry) {
+ return scheduleRetryInternal(objectType, objectId, attemptId, paymentControlPluginNames, timeOfRetry, null);
}
- private boolean scheduleRetryInternal(final ObjectType objectType, final UUID objectId, final UUID attemptId, final String pluginName, final DateTime timeOfRetry, final EntitySqlDaoWrapperFactory<EntitySqlDao> transactionalDao) {
+ private boolean scheduleRetryInternal(final ObjectType objectType, final UUID objectId, final UUID attemptId, final List<String> paymentControlPluginNames, final DateTime timeOfRetry, final EntitySqlDaoWrapperFactory<EntitySqlDao> transactionalDao) {
final InternalCallContext context = createCallContextFromPaymentId(objectType, objectId);
try {
final NotificationQueue retryQueue = notificationQueueService.getNotificationQueue(DefaultPaymentService.SERVICE_NAME, getQueueName());
- final NotificationEvent key = new PaymentRetryNotificationKey(attemptId, pluginName);
+ final NotificationEvent key = new PaymentRetryNotificationKey(attemptId, paymentControlPluginNames);
if (retryQueue != null) {
if (transactionalDao == null) {
retryQueue.recordFutureNotification(timeOfRetry, key, context.getUserToken(), context.getAccountRecordId(), context.getTenantRecordId());
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 dc2e457..07574be 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
@@ -17,21 +17,30 @@
package org.killbill.billing.payment.retry;
import java.math.BigDecimal;
+import java.util.UUID;
+import org.killbill.billing.catalog.api.Currency;
import org.killbill.billing.retry.plugin.api.PriorPaymentControlResult;
public class DefaultPriorPaymentControlResult implements PriorPaymentControlResult {
private final boolean isAborted;
private final BigDecimal adjustedRetryAmount;
+ private final Currency adjustedCurrency;
+ private final UUID adjustedPaymentMethodId;
- public DefaultPriorPaymentControlResult(final boolean isAborted, final BigDecimal adjustedRetryAmount) {
+ public DefaultPriorPaymentControlResult(final boolean isAborted,
+ final BigDecimal adjustedRetryAmount,
+ final Currency adjustedCurrency,
+ final UUID adjustedPaymentMethodId) {
this.isAborted = isAborted;
this.adjustedRetryAmount = adjustedRetryAmount;
+ this.adjustedCurrency = adjustedCurrency;
+ this.adjustedPaymentMethodId = adjustedPaymentMethodId;
}
public DefaultPriorPaymentControlResult(final boolean isAborted) {
- this(isAborted, null);
+ this(isAborted, null, null, null);
}
@Override
@@ -43,4 +52,13 @@ public class DefaultPriorPaymentControlResult implements PriorPaymentControlResu
public BigDecimal getAdjustedAmount() {
return adjustedRetryAmount;
}
+
+ @Override
+ public Currency getAdjustedCurrency() {
+ return adjustedCurrency;
+ }
+ @Override
+ public UUID getAdjustedPaymentMethodId() {
+ return adjustedPaymentMethodId;
+ }
}
diff --git a/payment/src/main/java/org/killbill/billing/payment/retry/DefaultRetryService.java b/payment/src/main/java/org/killbill/billing/payment/retry/DefaultRetryService.java
index f08f43a..b7e94a5 100644
--- a/payment/src/main/java/org/killbill/billing/payment/retry/DefaultRetryService.java
+++ b/payment/src/main/java/org/killbill/billing/payment/retry/DefaultRetryService.java
@@ -16,6 +16,7 @@
package org.killbill.billing.payment.retry;
+import java.util.List;
import java.util.UUID;
import org.killbill.billing.callcontext.InternalCallContext;
@@ -45,8 +46,8 @@ public class DefaultRetryService extends BaseRetryService implements RetryServic
@Override
- public void retryPaymentTransaction(final UUID attemptId, final String pluginName, final InternalCallContext context) {
- processor.retryPaymentTransaction(attemptId, pluginName, context);
+ public void retryPaymentTransaction(final UUID attemptId, final List<String> paymentControlPluginNames, final InternalCallContext context) {
+ processor.retryPaymentTransaction(attemptId, paymentControlPluginNames, context);
}
public static class DefaultRetryServiceScheduler extends RetryServiceScheduler {
diff --git a/payment/src/main/java/org/killbill/billing/payment/retry/PaymentRetryNotificationKey.java b/payment/src/main/java/org/killbill/billing/payment/retry/PaymentRetryNotificationKey.java
index 50a3155..d2569fd 100644
--- a/payment/src/main/java/org/killbill/billing/payment/retry/PaymentRetryNotificationKey.java
+++ b/payment/src/main/java/org/killbill/billing/payment/retry/PaymentRetryNotificationKey.java
@@ -16,6 +16,7 @@
package org.killbill.billing.payment.retry;
+import java.util.List;
import java.util.UUID;
import org.killbill.notificationq.api.NotificationEvent;
@@ -26,20 +27,20 @@ import com.fasterxml.jackson.annotation.JsonProperty;
public class PaymentRetryNotificationKey implements NotificationEvent {
private final UUID attemptId;
- private final String pluginName;
+ private final List<String> paymentControlPluginNames;
@JsonCreator
public PaymentRetryNotificationKey(@JsonProperty("attemptId") UUID attemptId,
- @JsonProperty("pluginName") String pluginName) {
+ @JsonProperty("paymentControlPluginNames") List<String> paymentControlPluginNames) {
this.attemptId = attemptId;
- this.pluginName = pluginName;
+ this.paymentControlPluginNames = paymentControlPluginNames;
}
public UUID getAttemptId() {
return attemptId;
}
- public String getPluginName() {
- return pluginName;
+ public List<String> getPaymentControlPluginNames() {
+ return paymentControlPluginNames;
}
}
diff --git a/payment/src/main/java/org/killbill/billing/payment/retry/RetryService.java b/payment/src/main/java/org/killbill/billing/payment/retry/RetryService.java
index 9864e0a..ea320f1 100644
--- a/payment/src/main/java/org/killbill/billing/payment/retry/RetryService.java
+++ b/payment/src/main/java/org/killbill/billing/payment/retry/RetryService.java
@@ -18,6 +18,7 @@
package org.killbill.billing.payment.retry;
+import java.util.List;
import java.util.UUID;
import org.killbill.billing.callcontext.InternalCallContext;
@@ -35,5 +36,5 @@ public interface RetryService {
public String getQueueName();
- public void retryPaymentTransaction(final UUID attemptId, String pluginName, final InternalCallContext context);
+ public void retryPaymentTransaction(final UUID attemptId, List<String> paymentControlPluginNames, final InternalCallContext context);
}
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 5689d8f..b184656 100644
--- a/payment/src/main/resources/org/killbill/billing/payment/ddl.sql
+++ b/payment/src/main/resources/org/killbill/billing/payment/ddl.sql
@@ -14,7 +14,7 @@ CREATE TABLE payment_attempts (
state_name varchar(32) NOT NULL,
amount numeric(15,9),
currency char(3),
- plugin_name varchar(50) NOT NULL,
+ plugin_name varchar(1024) NOT NULL,
plugin_properties blob(8194),
created_by varchar(50) NOT NULL,
created_date datetime NOT NULL,
@@ -45,7 +45,7 @@ CREATE TABLE payment_attempt_history (
state_name varchar(32) NOT NULL,
amount numeric(15,9),
currency char(3),
- plugin_name varchar(50) NOT NULL,
+ plugin_name varchar(1024) NOT NULL,
plugin_properties blob(8194),
change_type char(6) NOT NULL,
created_by varchar(50) NOT NULL,
diff --git a/payment/src/test/java/org/killbill/billing/payment/api/TestPaymentApi.java b/payment/src/test/java/org/killbill/billing/payment/api/TestPaymentApi.java
index ce075e3..38eb6ad 100644
--- a/payment/src/test/java/org/killbill/billing/payment/api/TestPaymentApi.java
+++ b/payment/src/test/java/org/killbill/billing/payment/api/TestPaymentApi.java
@@ -57,8 +57,8 @@ public class TestPaymentApi extends PaymentTestSuiteWithEmbeddedDB {
}
@Override
- public String getPaymentControlPluginName() {
- return InvoicePaymentControlPluginApi.PLUGIN_NAME;
+ public List<String> getPaymentControlPluginNames() {
+ return ImmutableList.<String>of(InvoicePaymentControlPluginApi.PLUGIN_NAME);
}
};
diff --git a/payment/src/test/java/org/killbill/billing/payment/api/TestPaymentApiNoDB.java b/payment/src/test/java/org/killbill/billing/payment/api/TestPaymentApiNoDB.java
index 22454ae..fd55a2c 100644
--- a/payment/src/test/java/org/killbill/billing/payment/api/TestPaymentApiNoDB.java
+++ b/payment/src/test/java/org/killbill/billing/payment/api/TestPaymentApiNoDB.java
@@ -57,8 +57,8 @@ public class TestPaymentApiNoDB extends PaymentTestSuiteNoDB {
return false;
}
@Override
- public String getPaymentControlPluginName() {
- return InvoicePaymentControlPluginApi.PLUGIN_NAME;
+ public List<String> getPaymentControlPluginNames() {
+ return ImmutableList.<String>of(InvoicePaymentControlPluginApi.PLUGIN_NAME);
}
};
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 992e210..918c9b1 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
@@ -17,6 +17,7 @@
package org.killbill.billing.payment.core.sm;
import java.math.BigDecimal;
+import java.util.List;
import java.util.UUID;
import java.util.concurrent.ExecutorService;
@@ -48,6 +49,8 @@ import org.killbill.bus.api.PersistentBus;
import org.killbill.clock.Clock;
import org.killbill.commons.locker.GlobalLocker;
+import com.google.common.collect.ImmutableList;
+
import static org.killbill.billing.payment.glue.PaymentModule.PLUGIN_EXECUTOR_NAMED;
import static org.killbill.billing.payment.glue.PaymentModule.RETRYABLE_NAMED;
@@ -77,10 +80,10 @@ public class MockRetryablePaymentAutomatonRunner extends PluginControlledPayment
@Nullable final UUID paymentId, @Nullable final String paymentExternalKey, final String paymentTransactionExternalKey,
@Nullable final BigDecimal amount, @Nullable final Currency currency,
final Iterable<PluginProperty> properties,
- final String pluginName, final CallContext callContext, final InternalCallContext internalCallContext) throws PaymentApiException {
+ final List<String> pluginNames, final CallContext callContext, final InternalCallContext internalCallContext) throws PaymentApiException {
if (context == null) {
return super.createContext(isApiPayment, transactionType, account, paymentMethodId, paymentId, paymentExternalKey, paymentTransactionExternalKey,
- amount, currency, properties, pluginName, callContext, internalCallContext);
+ amount, currency, properties, pluginNames, callContext, internalCallContext);
} else {
return context;
}
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 5acf9df..71cb99e 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
@@ -167,7 +167,7 @@ public class TestRetryablePayment extends PaymentTestSuiteNoDB {
eventBus);
paymentStateContext =
- new RetryablePaymentStateContext(MockPaymentControlProviderPlugin.PLUGIN_NAME,
+ new RetryablePaymentStateContext(ImmutableList.<String>of(MockPaymentControlProviderPlugin.PLUGIN_NAME),
true,
null,
paymentExternalKey,
@@ -632,7 +632,7 @@ public class TestRetryablePayment extends PaymentTestSuiteNoDB {
new PaymentTransactionModelDao(transactionId, attempt.getId(), paymentTransactionExternalKey, utcNow, utcNow, paymentId, TransactionType.AUTHORIZE, utcNow, TransactionStatus.PAYMENT_FAILURE, amount, currency, "bla", "foo"),
internalCallContext);
- processor.retryPaymentTransaction(attempt.getId(), MockPaymentControlProviderPlugin.PLUGIN_NAME, internalCallContext);
+ processor.retryPaymentTransaction(attempt.getId(), ImmutableList.<String>of(MockPaymentControlProviderPlugin.PLUGIN_NAME), internalCallContext);
final List<PaymentAttemptModelDao> pas = paymentDao.getPaymentAttemptByTransactionExternalKey(paymentTransactionExternalKey, internalCallContext);
assertEquals(pas.size(), 2);
@@ -676,7 +676,7 @@ public class TestRetryablePayment extends PaymentTestSuiteNoDB {
internalCallContext
);
- processor.retryPaymentTransaction(attempt.getId(), MockPaymentControlProviderPlugin.PLUGIN_NAME, internalCallContext);
+ processor.retryPaymentTransaction(attempt.getId(), ImmutableList.<String>of(MockPaymentControlProviderPlugin.PLUGIN_NAME), internalCallContext);
final List<PaymentAttemptModelDao> pas = paymentDao.getPaymentAttemptByTransactionExternalKey(paymentTransactionExternalKey, internalCallContext);
assertEquals(pas.size(), 2);
@@ -724,7 +724,7 @@ public class TestRetryablePayment extends PaymentTestSuiteNoDB {
internalCallContext
);
- processor.retryPaymentTransaction(attempt.getId(), MockPaymentControlProviderPlugin.PLUGIN_NAME, internalCallContext);
+ processor.retryPaymentTransaction(attempt.getId(), ImmutableList.<String>of(MockPaymentControlProviderPlugin.PLUGIN_NAME), internalCallContext);
final List<PaymentAttemptModelDao> pas = paymentDao.getPaymentAttemptByTransactionExternalKey(paymentTransactionExternalKey, internalCallContext);
assertEquals(pas.size(), 2);
diff --git a/payment/src/test/java/org/killbill/billing/payment/dao/TestPaymentDao.java b/payment/src/test/java/org/killbill/billing/payment/dao/TestPaymentDao.java
index 69045eb..67679de 100644
--- a/payment/src/test/java/org/killbill/billing/payment/dao/TestPaymentDao.java
+++ b/payment/src/test/java/org/killbill/billing/payment/dao/TestPaymentDao.java
@@ -64,7 +64,7 @@ public class TestPaymentDao extends PaymentTestSuiteWithEmbeddedDB {
final byte[] serialized = PluginPropertySerializer.serialize(properties);
final PaymentAttemptModelDao attempt = new PaymentAttemptModelDao(UUID.randomUUID(), UUID.randomUUID(), clock.getUTCNow(), clock.getUTCNow(),
paymentExternalKey, transactionId, transactionExternalKey, transactionType, stateName,
- BigDecimal.ZERO, Currency.ALL, pluginName, serialized);
+ BigDecimal.ZERO, Currency.ALL, ImmutableList.<String>of(pluginName), serialized);
PaymentAttemptModelDao savedAttempt = paymentDao.insertPaymentAttemptWithProperties(attempt, internalCallContext);
assertEquals(savedAttempt.getTransactionExternalKey(), transactionExternalKey);
diff --git a/payment/src/test/java/org/killbill/billing/payment/provider/MockPaymentControlProviderPlugin.java b/payment/src/test/java/org/killbill/billing/payment/provider/MockPaymentControlProviderPlugin.java
index fc0b8a3..6a6e347 100644
--- a/payment/src/test/java/org/killbill/billing/payment/provider/MockPaymentControlProviderPlugin.java
+++ b/payment/src/test/java/org/killbill/billing/payment/provider/MockPaymentControlProviderPlugin.java
@@ -44,7 +44,7 @@ public class MockPaymentControlProviderPlugin implements PaymentControlPluginApi
@Override
public PriorPaymentControlResult priorCall(final PaymentControlContext paymentControlContext) throws PaymentControlApiException{
- return new DefaultPriorPaymentControlResult(isAborted, null);
+ return new DefaultPriorPaymentControlResult(isAborted, null, null, null);
}
@Override
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 3281965..485e449 100644
--- a/payment/src/test/java/org/killbill/billing/payment/TestJanitor.java
+++ b/payment/src/test/java/org/killbill/billing/payment/TestJanitor.java
@@ -48,6 +48,7 @@ import org.testng.annotations.BeforeClass;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
+import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.inject.Inject;
@@ -62,8 +63,8 @@ public class TestJanitor extends PaymentTestSuiteWithEmbeddedDB {
}
@Override
- public String getPaymentControlPluginName() {
- return InvoicePaymentControlPluginApi.PLUGIN_NAME;
+ public List<String> getPaymentControlPluginNames() {
+ return ImmutableList.of(InvoicePaymentControlPluginApi.PLUGIN_NAME);
}
};
diff --git a/payment/src/test/java/org/killbill/billing/payment/TestRetryService.java b/payment/src/test/java/org/killbill/billing/payment/TestRetryService.java
index 9b9537e..54744ef 100644
--- a/payment/src/test/java/org/killbill/billing/payment/TestRetryService.java
+++ b/payment/src/test/java/org/killbill/billing/payment/TestRetryService.java
@@ -144,7 +144,7 @@ public class TestRetryService extends PaymentTestSuiteNoDB {
final String transactionExternalKey = UUID.randomUUID().toString();
try {
pluginControlledPaymentProcessor.createPurchase(false, account, account.getPaymentMethodId(), null, amount, Currency.USD, paymentExternalKey, transactionExternalKey,
- createPropertiesForInvoice(invoice), InvoicePaymentControlPluginApi.PLUGIN_NAME, callContext, internalCallContext);
+ createPropertiesForInvoice(invoice), ImmutableList.<String>of(InvoicePaymentControlPluginApi.PLUGIN_NAME), callContext, internalCallContext);
} catch (final PaymentApiException e) {
failed = true;
}
diff --git a/util/src/main/java/org/killbill/billing/util/config/PaymentConfig.java b/util/src/main/java/org/killbill/billing/util/config/PaymentConfig.java
index 6100e6d..91f75c0 100644
--- a/util/src/main/java/org/killbill/billing/util/config/PaymentConfig.java
+++ b/util/src/main/java/org/killbill/billing/util/config/PaymentConfig.java
@@ -80,6 +80,11 @@ public interface PaymentConfig extends KillbillConfig {
@Description("Rate at which janitor tasks are scheduled")
public TimeSpan getJanitorRunningRate();
+ @Config("org.killbill.payment.control.plugin")
+ @Default("__INVOICE_PAYMENT_CONTROL_PLUGIN__")
+ @Description("Whether the payment subsystem is off")
+ public List<String> getPaymentControlPluginNames();
+
@Config("org.killbill.payment.off")
@Default("false")
@Description("Whether the payment subsystem is off")