Skip to content

Commit

Permalink
Implement adjusting plugin properties for all control plugin api call…
Browse files Browse the repository at this point in the history
…s. The semantics is the same as the one we chose for the other parameters that can be adjusted (paymentMethodId, amount, ...),

where we update the value at each iteration (for each plugin) when it is not null. For plugin properties, it means each plugin should either return null (nothing to adjust) or only modify/delete/update
the value it cares about.
  • Loading branch information
sbrossie committed Aug 5, 2015
1 parent d9c9abc commit c8a2d2f
Show file tree
Hide file tree
Showing 4 changed files with 88 additions and 67 deletions.
Expand Up @@ -59,7 +59,6 @@ public PluginDispatcherReturnType<OperationResult> doOperation() throws Operatio
transaction.getCurrency(), transaction.getCurrency(),
transaction.getProcessedAmount(), transaction.getProcessedAmount(),
transaction.getProcessedCurrency(), transaction.getProcessedCurrency(),
paymentStateContext.getProperties(),
paymentStateControlContext.isApiPayment(), paymentStateControlContext.isApiPayment(),
paymentStateContext.getCallContext()); paymentStateContext.getCallContext());


Expand Down
Expand Up @@ -32,6 +32,7 @@
import org.killbill.billing.account.api.Account; import org.killbill.billing.account.api.Account;
import org.killbill.billing.callcontext.DefaultCallContext; import org.killbill.billing.callcontext.DefaultCallContext;
import org.killbill.billing.catalog.api.Currency; 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.osgi.api.OSGIServiceRegistration;
import org.killbill.billing.payment.api.Payment; import org.killbill.billing.payment.api.Payment;
import org.killbill.billing.payment.api.PaymentApiException; import org.killbill.billing.payment.api.PaymentApiException;
Expand All @@ -50,6 +51,7 @@
import org.killbill.billing.control.plugin.api.PaymentControlContext; import org.killbill.billing.control.plugin.api.PaymentControlContext;
import org.killbill.billing.control.plugin.api.PaymentControlPluginApi; import org.killbill.billing.control.plugin.api.PaymentControlPluginApi;
import org.killbill.billing.control.plugin.api.PriorPaymentControlResult; 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.billing.util.callcontext.CallContext;
import org.killbill.commons.locker.GlobalLocker; import org.killbill.commons.locker.GlobalLocker;
import org.killbill.commons.locker.LockFailedException; import org.killbill.commons.locker.LockFailedException;
Expand Down Expand Up @@ -92,7 +94,6 @@ public PluginDispatcherReturnType<OperationResult> doOperation() throws Operatio
paymentStateContext.getTransactionType(), paymentStateContext.getTransactionType(),
paymentStateContext.getAmount(), paymentStateContext.getAmount(),
paymentStateContext.getCurrency(), paymentStateContext.getCurrency(),
paymentStateContext.getProperties(),
paymentStateControlContext.isApiPayment(), paymentStateControlContext.isApiPayment(),
paymentStateContext.getCallContext()); paymentStateContext.getCallContext());


Expand All @@ -110,9 +111,6 @@ public PluginDispatcherReturnType<OperationResult> doOperation() throws Operatio


final boolean success; final boolean success;
try { try {
// Adjust amount with value returned by plugin if necessary
adjustStateContextValues(paymentStateContext, pluginResult);

final Payment result = doCallSpecificOperationCallback(); final Payment result = doCallSpecificOperationCallback();
((PaymentStateControlContext) paymentStateContext).setResult(result); ((PaymentStateControlContext) paymentStateContext).setResult(result);
final PaymentTransaction transaction = ((PaymentStateControlContext) paymentStateContext).getCurrentTransaction(); final PaymentTransaction transaction = ((PaymentStateControlContext) paymentStateContext).getCurrentTransaction();
Expand All @@ -131,7 +129,6 @@ public PluginDispatcherReturnType<OperationResult> doOperation() throws Operatio
transaction.getCurrency(), transaction.getCurrency(),
transaction.getProcessedAmount(), transaction.getProcessedAmount(),
transaction.getProcessedCurrency(), transaction.getProcessedCurrency(),
paymentStateContext.getProperties(),
paymentStateControlContext.isApiPayment(), paymentStateControlContext.isApiPayment(),
paymentStateContext.getCallContext()); paymentStateContext.getCallContext());


Expand Down Expand Up @@ -173,40 +170,6 @@ protected OperationException unwrapExceptionFromDispatchedTask(final PaymentStat
return new OperationException(e, getOperationResultOnException(paymentStateContext)); 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) { private OperationResult getOperationResultOnException(final PaymentStateContext paymentStateContext) {
final PaymentStateControlContext paymentStateControlContext = (PaymentStateControlContext) paymentStateContext; final PaymentStateControlContext paymentStateControlContext = (PaymentStateControlContext) paymentStateContext;
final OperationResult operationResult = paymentStateControlContext.getRetryDate() != null ? OperationResult.FAILURE : OperationResult.EXCEPTION; final OperationResult operationResult = paymentStateControlContext.getRetryDate() != null ? OperationResult.FAILURE : OperationResult.EXCEPTION;
Expand All @@ -217,7 +180,9 @@ private PriorPaymentControlResult executePluginPriorCalls(final List<String> pay
// Return as soon as the first plugin aborts, or the last result for the last plugin // Return as soon as the first plugin aborts, or the last result for the last plugin
PriorPaymentControlResult prevResult = null; PriorPaymentControlResult prevResult = null;


// Those values are adjusted prior each call with the result of what previous call to plugin returned
PaymentControlContext inputPaymentControlContext = paymentControlContextArg; PaymentControlContext inputPaymentControlContext = paymentControlContextArg;
Iterable<PluginProperty> inputPluginProperties = paymentStateContext.getProperties();


for (final String pluginName : paymentControlPluginNames) { for (final String pluginName : paymentControlPluginNames) {
final PaymentControlPluginApi plugin = paymentControlPluginRegistry.getServiceForName(pluginName); final PaymentControlPluginApi plugin = paymentControlPluginRegistry.getServiceForName(pluginName);
Expand All @@ -226,7 +191,10 @@ private PriorPaymentControlResult executePluginPriorCalls(final List<String> pay
logger.warn("Skipping unknown payment control plugin {} when fetching results", pluginName); logger.warn("Skipping unknown payment control plugin {} when fetching results", pluginName);
continue; continue;
} }
prevResult = plugin.priorCall(inputPaymentControlContext, paymentStateContext.getProperties()); prevResult = plugin.priorCall(inputPaymentControlContext, inputPluginProperties);
if (prevResult.getAdjustedPluginProperties() != null) {
inputPluginProperties = prevResult.getAdjustedPluginProperties();
}
if (prevResult.isAborted()) { if (prevResult.isAborted()) {
break; break;
} }
Expand All @@ -239,14 +207,36 @@ private PriorPaymentControlResult executePluginPriorCalls(final List<String> pay
paymentStateContext.getTransactionType(), paymentStateContext.getTransactionType(),
prevResult.getAdjustedAmount() != null ? prevResult.getAdjustedAmount() : inputPaymentControlContext.getAmount(), prevResult.getAdjustedAmount() != null ? prevResult.getAdjustedAmount() : inputPaymentControlContext.getAmount(),
prevResult.getAdjustedCurrency() != null ? prevResult.getAdjustedCurrency() : inputPaymentControlContext.getCurrency(), prevResult.getAdjustedCurrency() != null ? prevResult.getAdjustedCurrency() : inputPaymentControlContext.getCurrency(),
prevResult.getAdjustedPluginProperties() != null ? prevResult.getAdjustedPluginProperties() : inputPaymentControlContext.getPluginProperties(),
paymentStateControlContext.isApiPayment(), paymentStateControlContext.isApiPayment(),
paymentStateContext.getCallContext()); 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; 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) { private OperationResult executePluginOnFailureCallsAndSetRetryDate(final PaymentStateControlContext paymentStateControlContext, final PaymentControlContext paymentControlContext) {
final DateTime retryDate = executePluginOnFailureCalls(paymentStateControlContext.getPaymentControlPluginNames(), paymentControlContext); final DateTime retryDate = executePluginOnFailureCalls(paymentStateControlContext.getPaymentControlPluginNames(), paymentControlContext);
if (retryDate != null) { if (retryDate != null) {
Expand All @@ -256,26 +246,61 @@ private OperationResult executePluginOnFailureCallsAndSetRetryDate(final Payment
} }


private DateTime executePluginOnFailureCalls(final List<String> paymentControlPluginNames, final PaymentControlContext paymentControlContext) { private DateTime executePluginOnFailureCalls(final List<String> paymentControlPluginNames, final PaymentControlContext paymentControlContext) {

DateTime candidate = null; DateTime candidate = null;
Iterable<PluginProperty> inputPluginProperties = paymentStateContext.getProperties();

for (final String pluginName : paymentControlPluginNames) { for (final String pluginName : paymentControlPluginNames) {
final PaymentControlPluginApi plugin = paymentControlPluginRegistry.getServiceForName(pluginName); final PaymentControlPluginApi plugin = paymentControlPluginRegistry.getServiceForName(pluginName);
if (plugin != null) { if (plugin != null) {
try { try {
final OnFailurePaymentControlResult result = plugin.onFailureCall(paymentControlContext, paymentStateContext.getProperties()); final OnFailurePaymentControlResult result = plugin.onFailureCall(paymentControlContext, inputPluginProperties);
if (candidate == null) { if (candidate == null) {
candidate = result.getNextRetryDate(); candidate = result.getNextRetryDate();
} else if (result.getNextRetryDate() != null) { } else if (result.getNextRetryDate() != null) {
candidate = candidate.compareTo(result.getNextRetryDate()) > 0 ? result.getNextRetryDate() : candidate; candidate = candidate.compareTo(result.getNextRetryDate()) > 0 ? result.getNextRetryDate() : candidate;
} }

if (result.getAdjustedPluginProperties() != null) {
inputPluginProperties = result.getAdjustedPluginProperties();
}

} catch (final PaymentControlApiException e) { } catch (final PaymentControlApiException e) {
logger.warn("Plugin " + pluginName + " failed to return next retryDate for payment " + paymentControlContext.getPaymentExternalKey(), e); logger.warn("Plugin " + pluginName + " failed to return next retryDate for payment " + paymentControlContext.getPaymentExternalKey(), e);
return candidate; return candidate;
} }
} }
} }
adjustStateContextPluginProperties(paymentStateContext, inputPluginProperties);
return candidate; 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 { public static class DefaultPaymentControlContext extends DefaultCallContext implements PaymentControlContext {


private final Account account; private final Account account;
Expand All @@ -291,15 +316,14 @@ public static class DefaultPaymentControlContext extends DefaultCallContext impl
private final BigDecimal processedAmount; private final BigDecimal processedAmount;
private final Currency processedCurrency; private final Currency processedCurrency;
private final boolean isApiPayment; 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, 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) { final boolean isApiPayment, final CallContext callContext) {
this(account, paymentMethodId, attemptId, paymentId, paymentExternalKey, null, transactionExternalKey, transactionType, amount, currency, null, null, properties, isApiPayment, 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, 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()); super(callContext.getTenantId(), callContext.getUserName(), callContext.getCallOrigin(), callContext.getUserType(), callContext.getReasonCode(), callContext.getComments(), callContext.getUserToken(), callContext.getCreatedDate(), callContext.getUpdatedDate());
this.account = account; this.account = account;
this.paymentMethodId = paymentMethodId; this.paymentMethodId = paymentMethodId;
Expand All @@ -313,7 +337,6 @@ public DefaultPaymentControlContext(final Account account, final UUID paymentMet
this.currency = currency; this.currency = currency;
this.processedAmount = processedAmount; this.processedAmount = processedAmount;
this.processedCurrency = processedCurrency; this.processedCurrency = processedCurrency;
this.properties = properties;
this.isApiPayment = isApiPayment; this.isApiPayment = isApiPayment;
} }


Expand Down Expand Up @@ -381,11 +404,6 @@ public UUID getTransactionId() {
return transactionId; return transactionId;
} }


@Override
public Iterable<PluginProperty> getPluginProperties() {
return properties;
}

@Override @Override
public String toString() { public String toString() {
return "DefaultPaymentControlContext{" + return "DefaultPaymentControlContext{" +
Expand All @@ -402,7 +420,6 @@ public String toString() {
", processedAmount=" + processedAmount + ", processedAmount=" + processedAmount +
", processedCurrency=" + processedCurrency + ", processedCurrency=" + processedCurrency +
", isApiPayment=" + isApiPayment + ", isApiPayment=" + isApiPayment +
", properties=" + properties +
'}'; '}';
} }
} }
Expand Down

0 comments on commit c8a2d2f

Please sign in to comment.